您的位置:首页 > 移动开发 > Android开发

Android input 输入系统学习

2015-09-19 11:35 555 查看

Android Input 输入系统学习

Android输入设备的映射

使用指令dumpsys input 可以看到以下信息
130|shell@cv6a638_base:/ # dumpsys input
INPUT MANAGER (dumpsys input)

Event Hub State:
BuiltInKeyboardId: -2
Devices:
-1: Virtual
Classes: 0x40000023
Path: <virtual>
Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd
Location:
ControllerNumber: 0
UniqueId: <virtual>
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
1: MStar Smart TV Keypad
Classes: 0x00000001
Path: /dev/input/event2
Descriptor: 8f43d929a9472e8dc54d48a6c41e2435e8eaff35
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0006, vendor=0x3697, product=0x0002, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0002.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
2: MCE IR Keyboard/Mouse (ir)
Classes: 0x0000000b
Path: /dev/input/event1
Descriptor: 2b764a30c0f74e1362d8ef86c5e4f12150af666f
Location: /input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
3: MStar Smart TV IR Receiver
Classes: 0x00000001
Path: /dev/input/event0
Descriptor: 0e50bdc18d3ae0b6f247100cbd99062d93c208eb
Location: /dev/ir
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0018, vendor=0x3697, product=0x0001, version=0x0001
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0001.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
4: AVRCP
Classes: 0x80000001
Path: /dev/input/event3
Descriptor: 89065b1db3afd7f700b4049a883d405d76233a40
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0005, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/AVRCP.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false

Input Reader State:
Device -1: Virtual
Generation: 2
IsExternal: false
Sources: 0x00000301
KeyboardType: 2
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
KeyboardType: 2
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 0
Device 1: MStar Smart TV Keypad
Generation: 9
IsExternal: false
Sources: 0x00000101
KeyboardType: 1
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
KeyboardType: 1
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 0
Device 2: MCE IR Keyboard/Mouse (ir)
Generation: 11
IsExternal: false
Sources: 0x00002103
KeyboardType: 2
Motion Ranges:
X: source=0x00002002, min=0.000, max=1919.000, flat=0.000, fuzz=0.000, resolution=0.000
Y: source=0x00002002, min=0.000, max=1079.000, flat=0.000, fuzz=0.000, resolution=0.000
PRESSURE: source=0x00002002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
KeyboardType: 2
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 0
Cursor Input Mapper:
Parameters:
HasAssociatedDisplay: true
Mode: pointer
OrientationAware: false
XScale: 1.000
YScale: 1.000
XPrecision: 1.000
YPrecision: 1.000
HaveVWheel: false
HaveHWheel: false
VWheelScale: 1.000
HWheelScale: 1.000
Orientation: 0
ButtonState: 0x00000000
Down: false
DownTime: 0
Device 3: MStar Smart TV IR Receiver
Generation: 4
IsExternal: false
Sources: 0x00000101
KeyboardType: 1
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
KeyboardType: 1
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 0
Device 4: AVRCP
Generation: 12
IsExternal: true
Sources: 0x00000101
KeyboardType: 1
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
KeyboardType: 1
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 0
Configuration:
ExcludedDeviceNames: []
VirtualKeyQuietTime: 0.0ms
PointerVelocityControlParameters: scale=1.000, lowThreshold=500.000, highThreshold=3000.000, acceleration=3.000
WheelVelocityControlParameters: scale=1.000, lowThreshold=15.000, highThreshold=50.000, acceleration=4.000
PointerGesture:
Enabled: true
QuietInterval: 100.0ms
DragMinSwitchSpeed: 50.0px/s
TapInterval: 150.0ms
TapDragInterval: 300.0ms
TapSlop: 20.0px
MultitouchSettleInterval: 100.0ms
MultitouchMinDistance: 15.0px
SwipeTransitionAngleCosine: 0.3
SwipeMaxWidthRatio: 0.2
MovementSpeedRatio: 0.8
ZoomSpeedRatio: 0.3

Input Dispatcher State:
DispatchEnabled: 1
DispatchFrozen: 0
FocusedApplication: name='AppWindowToken{42534628 token=Token{42532cb8 ActivityRecord{4255e728 u0 com.assem.launcher/.MainActivity t1}}}', dispatchingTimeout=5000.000ms
FocusedWindow: name='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}'
TouchDown: false
TouchSplit: false
TouchDeviceId: -1
TouchSource: 0x00000000
TouchDisplayId: -1
TouchedWindows: <none>
Windows:
0: name='Window{424d0270 u0 NotificationPanel}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01820300, type=0x000007e8, layer=211000, frame=[1152,-75][1920,1080], scale=1.000000, touchableRegion=[1152,-75][1920,1080], inputFeatures=0x00000000, ownerPid=2137, ownerUid=10003, dispatchingTimeout=5000.000ms
1: name='Window{42514f48 u0 KeyguardScrim}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x00110900, type=0x000007ed, layer=121000, frame=[0,0][1920,1080], scale=1.000000, touchableRegion=[0,0][1920,1080], inputFeatures=0x00000000, ownerPid=2067, ownerUid=1000, dispatchingTimeout=5000.000ms
2: name='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}', displayId=0, paused=false, hasFocus=true, hasWallpaper=false, visible=true, canReceiveKeys=true, flags=0x01810520, type=0x00000001, layer=21005, frame=[0,0][1920,1080], scale=1.000000, touchableRegion=[0,0][1920,1080], inputFeatures=0x00000000, ownerPid=2301, ownerUid=1000, dispatchingTimeout=5000.000ms
3: name='Window{424c7c38 u0 com.android.systemui.ImageWallpaper}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00000318, type=0x000007dd, layer=21000, frame=[0,0][1920,1280], scale=1.000000, touchableRegion=[0,0][1920,1280], inputFeatures=0x00000000, ownerPid=2137, ownerUid=10003, dispatchingTimeout=5000.000ms
MonitoringChannels:
0: 'WindowManager (server)'
RecentQueue: length=5
DeviceResetEvent(deviceId=-1), policyFlags=0x00000000, age=2045547.2ms
DeviceResetEvent(deviceId=3), policyFlags=0x00000000, age=2045547.2ms
DeviceResetEvent(deviceId=2), policyFlags=0x00000000, age=2045547.2ms
DeviceResetEvent(deviceId=1), policyFlags=0x00000000, age=2045547.2ms
DeviceResetEvent(deviceId=4), policyFlags=0x00000000, age=2033442.2ms
PendingEvent: <none>
InboundQueue: <empty>
Connections:
0: channelName='425a9768 com.assem.launcher/com.assem.launcher.MainActivity (server)', windowName='Window{425a9768 u0 com.assem.launcher/com.assem.launcher.MainActivity}', status=NORMAL, monitor=false, inputPublisherBlocked=false
OutboundQueue: <empty>
WaitQueue: <empty>
1: channelName='WindowManager (server)', windowName='monitor', status=NORMAL, monitor=true, inputPublisherBlocked=false
OutboundQueue: <empty>
WaitQueue: <empty>
2: channelName='42514f48 KeyguardScrim (server)', windowName='Window{42514f48 u0 KeyguardScrim}', status=NORMAL, monitor=false, inputPublisherBlocked=false
OutboundQueue: <empty>
WaitQueue: <empty>
3: channelName='424d0270 NotificationPanel (server)', windowName='Window{424d0270 u0 NotificationPanel}', status=NORMAL, monitor=false, inputPublisherBlocked=false
OutboundQueue: <empty>
WaitQueue: <empty>
4: channelName='424c7c38 com.android.systemui.ImageWallpaper (server)', windowName='Window{424c7c38 u0 com.android.systemui.ImageWallpaper}', status=NORMAL, monitor=false, inputPublisherBlocked=false
OutboundQueue: <empty>
WaitQueue: <empty>
AppSwitch: not pending
Configuration:
KeyRepeatDelay: 50.0ms
KeyRepeatTimeout: 500.0ms

shell@cv6a638_base:/ #
我们可以看到在KeyLayoutFile条目中看到加载的按键映射文件,都是根据厂商ID和产品ID匹配对应文件的,例如
vendor为0x3697,product 为0x0001,那么对应的配置文件为/system/usr/keylayout/Vendor_3697_Product_0001.kl
另外该命令还很多信息可挖,例如KeyRepeatDelay: 50.0ms,KeyRepeatTimeout: 500.0ms在按键性能优化时,就可以起到作用。

映射文件的加载

从上面的命令可以整体把握按键的映射情况,但是如何加载,我们可以看一下按键的处理枢纽,也即EventHub.
首先EventHub在启动之后会去搜索/dev/input目录下的设备文件,并读取相关设备信息, 主要用

status_t EventHub::scanDirLocked(const char *dirname)
{
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);
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}

openDeviceLocked分别会去读取/dev/input 目录下信息,如下:

shell@cv6a638_base:/ # ls /dev/input/ -al
crw-rw---- root     input     13,  64 1970-01-01 08:00 event0
crw-rw---- root     input     13,  65 1970-01-01 08:00 event1
crw-rw---- root     input     13,  66 1970-01-01 08:00 event2
crw-rw---- root     input     13,  67 1970-01-02 08:01 event3
crw-rw---- root     input     13,   0 1970-01-01 08:00 js0
crw-rw---- root     input     13,  63 1970-01-01 08:00 mice
crw-rw---- root     input     13,  32 1970-01-01 08:00 mouse0
这些设备中有鼠标,键盘,遥控器,滚动球等等,详细映射如下:
Event Hub State:
BuiltInKeyboardId: -2
Devices:
-1: Virtual
Classes: 0x40000023
Path: <virtual>
Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd
Location:
ControllerNumber: 0
UniqueId: <virtual>
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
1: MStar Smart TV Keypad
Classes: 0x00000001
Path: /dev/input/event2
Descriptor: 8f43d929a9472e8dc54d48a6c41e2435e8eaff35
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0006, vendor=0x3697, product=0x0002, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0002.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
2: MCE IR Keyboard/Mouse (ir)
Classes: 0x0000000b
Path: /dev/input/event1
Descriptor: 2b764a30c0f74e1362d8ef86c5e4f12150af666f
Location: /input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/Generic.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
3: MStar Smart TV IR Receiver
Classes: 0x00000001
Path: /dev/input/event0
Descriptor: 0e50bdc18d3ae0b6f247100cbd99062d93c208eb
Location: /dev/ir
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0018, vendor=0x3697, product=0x0001, version=0x0001
KeyLayoutFile: /system/usr/keylayout/Vendor_3697_Product_0001.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
4: AVRCP
Classes: 0x80000001
Path: /dev/input/event3
Descriptor: 89065b1db3afd7f700b4049a883d405d76233a40
Location:
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0005, vendor=0x0000, product=0x0000, version=0x0000
KeyLayoutFile: /system/usr/keylayout/AVRCP.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false
openDeviceLocked完成单个设备信息的读取,并组装成Device添加至mDevices(KeyedVector<int32_t, Device*>)中
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];

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

int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}

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);
}

// Check to see if the device is on our excluded list
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
ALOGI("ignoring event id %s driver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}

// Get device driver version.
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}

// Get device identifier.
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
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);
}

// 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);
}

// Fill in the descriptor.
setDescriptor(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);

ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV("  bus:        %04x\n"
"  vendor      %04x\n"
"  product     %04x\n"
"  version     %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
ALOGV("  name:       \"%s\"\n", identifier.name.string());
ALOGV("  location:   \"%s\"\n", identifier.location.string());
ALOGV("  unique id:  \"%s\"\n", identifier.uniqueId.string());
ALOGV("  descriptor: \"%s\"\n", identifier.descriptor.string());
ALOGV("  driver:     v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);

// Load the configuration file for the device.
loadConfigurationLocked(device);

// Figure out the kinds of events the device reports.
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);
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

// 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.
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}

// See if this is a cursor device such as a trackball or mouse.
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}

// MStar Android Patch Begin
// See if this is a cursor device of cywee company
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)
&& test_bit(REL_RX, device->relBitmask)
&& test_bit(REL_RY, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CYWEE_CURSOR;
}
// MStar Android Patch End

// See if this is a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// 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.
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touch driver?
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
&& test_bit(ABS_X, device->absBitmask)
&& test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
}

// 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.
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;
}
}
}

// 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;
}
}

// Check whether this device supports the vibrator.
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
}

// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// 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.
status_t status = loadVirtualKeyMapLocked(device);
if (!status) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
}

// 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)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}

// Configure the keyboard, gamepad or virtual keyboard.
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}

// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}

// See if this device has a DPAD.
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)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}

// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
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.
if (device->classes == 0) {
ALOGV("Dropping device: id=%d, path='%s', name='%s'",
deviceId, devicePath, device->identifier.name.string());
delete device;
return -1;
}

// Determine whether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}

if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_GAMEPAD)) {
device->controllerNumber = getNextControllerNumberLocked(device);
}

// 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)) {
ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
delete device;
return -1;
}

// Enable wake-lock behavior on kernels that support it.
// TODO: Only need this for devices that can really wake the system.
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.
int clockId = CLOCK_MONOTONIC;
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);

ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
"usingSuspendBlockIoctl=%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),
toString(usingSuspendBlockIoctl), toString(usingClockIoctl));

addDeviceLocked(device);
return 0;
}


使用ioctl(fd, EVIOCGID, &inputId)读取出设备标识信息赋给identifier,并创建Device,接着loadConfigurationLocked接在配置,最后使用addDeviceLocked添加设备信息。

按键布局文件(kl)加载

由上面分析指导,每个Device都对应一个设备,底层配置文件中的映射关系都需要load到android系统中才能被使用,我们可以先看EventHub的子类Device
struct Device {
Device* next;

int fd; // may be -1 if device is virtual
const int32_t id;
const String8 path;
const InputDeviceIdentifier identifier;

uint32_t classes;

uint8_t keyBitmask[(KEY_MAX + 1) / 8];
uint8_t absBitmask[(ABS_MAX + 1) / 8];
uint8_t relBitmask[(REL_MAX + 1) / 8];
uint8_t swBitmask[(SW_MAX + 1) / 8];
uint8_t ledBitmask[(LED_MAX + 1) / 8];
uint8_t ffBitmask[(FF_MAX + 1) / 8];
uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8];

String8 configurationFile;
PropertyMap* configuration;
VirtualKeyMap* virtualKeyMap;
KeyMap keyMap;

sp<KeyCharacterMap> overlayKeyMap;
sp<KeyCharacterMap> combinedKeyMap;

bool ffEffectPlaying;
int16_t ffEffectId; // initially -1

int32_t controllerNumber;

int32_t timestampOverrideSec;
int32_t timestampOverrideUsec;

Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();

void close();

inline bool isVirtual() const { return fd < 0; }

const sp<KeyCharacterMap>& getKeyCharacterMap() const {
if (combinedKeyMap != NULL) {
return combinedKeyMap;
}
return keyMap.keyCharacterMap;
}
}


子类Device中函数KeyMap keyMap,该对象就是一个按键映射对象,接着分析如何加载
// Load the keymap for the device.
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
其实进入loadKeyMapLocked有两各入口,createVirtualKeyboardLocked和openDeviceLocked,后者则是我们上面分析的一条路径。
device->keyMap.load(device->identifier, device->configuration)也即进入Device::load

status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}

String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}

if (isComplete()) {
return OK;
}
}

// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK;
}

// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
//      generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}

// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
return OK;
}

// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.string());
return NAME_NOT_FOUND;
}
接着进入loadKeyLayout
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}

status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
if (status) {
return status;
}

keyLayoutFile.setTo(path);
return OK;
}


/* Types of input device configuration files. */
enum InputDeviceConfigurationFileType {
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0,     /* .idc file */
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1,        /* .kl file */
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */
};
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT就代表着.kl 类型文件,紧接着进入KeyLayoutMap::load(path, &keyLayoutMap)

status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
outMap->clear();

Tokenizer* tokenizer;
status_t status = Tokenizer::open(filename, &tokenizer);
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.string());
} else {
sp<KeyLayoutMap> map = new KeyLayoutMap();
if (!map.get()) {
ALOGE("Error allocating key layout map.");
status = NO_MEMORY;
} else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
Parser parser(map.get(), tokenizer);
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (!status) {
*outMap = map;
}
}
delete tokenizer;
}
return status;
}


KeyLayoutMap::Parser完成.kl 文件内容的解析

status_t KeyLayoutMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
mTokenizer->peekRemainderOfLine().string());
#endif

mTokenizer->skipDelimiters(WHITESPACE);

if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
if (keywordToken == "key") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseKey();
if (status) return status;
} else if (keywordToken == "axis") {
mTokenizer->skipDelimiters(WHITESPACE);
status_t status = parseAxis();
if (status) return status;
} else {
ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
keywordToken.string());
return BAD_VALUE;
}

mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
mTokenizer->getLocation().string(),
mTokenizer->peekRemainderOfLine().string());
return BAD_VALUE;
}
}

mTokenizer->nextLine();
}
return NO_ERROR;
}


至此按键文件的配置,加载,解析流程完毕。
主要涉及几个核心类EventHub,KeyMap,KeyLayoutMap,InputDeviceInfo。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: