.kl文件的加载过程分析
.kl文件
简述
kl文件也就是keylayout文件,它的作用是将Linux scancode转换为Android keycode。scancode就是硬件直接扫描到的数字,而这些数字会通过这个kl文件对应到字符串,也就是keycode。
设备可以拥有自己专属的kl文件,命名规则和idc文件一样,这里就不重复说了。另外系统提供了一个特殊的内置常规按键布局文件,名为 Generic.kl。当找不到专属的kl时候就会用Generic.kl
示例
下面示例是Generic.kl中一些键值对类型。当然设备的专属kl文件并不需要包含下方的所有类型,只需要包含会用到的就可以了
/odm/usr/keylayout/Generic.kl
/vendor/usr/keylayout/Generic.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl
# 键盘
key 1 ESCAPE
key 2 1
key 3 2
key 12 MINUS
key 13 EQUALS
key 14 DEL
# 系统控件
key 114 VOLUME_DOWN
key 115 VOLUME_UP
key 116 POWER
#电容式按钮
key 139 MENU VIRTUAL
key 172 HOME VIRTUAL
key 158 BACK VIRTUAL
key 217 SEARCH VIRTUAL
#耳机插孔媒体控件
key 163 MEDIA_NEXT
key 165 MEDIA_PREVIOUS
key 226 HEADSETHOOK
#操纵杆
key 304 BUTTON_A
key 305 BUTTON_B
key 307 BUTTON_X
key 308 BUTTON_Y
# Keys defined by HID usages
key usage 0x0c006F BRIGHTNESS_UP
key usage 0x0c0070 BRIGHTNESS_DOWN
EventHub::openDeviceLocked
这个过程和加载idc一样也是从openDeviceLocked开始的,调用loadKeyMapLocked() 加载Kl文件
status_t EventHub::openDeviceLocked(const char* devicePath) {
char buffer[80];
// devicePath = /dev/input/eventx
int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
InputDeviceIdentifier identifier;
...
// 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);
}
...
return OK;
}
loadKeyMapLocked就直接调用keyMap.load了
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
KeyMap::load
还记得我们之前解析idc文件的时候有去解析"keyboard.layout"和"keyboard.characterMap"这两项吗,在这个函数开头我们就是判断在该设备的idc文件中有没有指定kl和kcm文件,如果有的话我们就用idc文件中指定的,如果没有我们再通过probeKeyMap() 去各个目录下查找
查找的话我们会先通过设备标识符去找它专属的kl文件,找不到就去找Generic,虽然有个最后的选择虚拟键盘映射,不过并不会用到它,因为Generic.kl总是存在的
\frameworks\native\libs\input\Keyboard.cpp
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.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
deviceIdenfifier.name.c_str(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.c_str(), keyLayoutName.string());
}
}
if (isComplete()) {
return OK;
}
}
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, "")) {
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, "Generic")) {
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, "Virtual")) {
return OK;
}
// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.c_str());
return NAME_NOT_FOUND;
}
KeyMap::probeKeyMap
接下来看一下probeKeyMap() 的实现,它会分别去加载kl和kcm文件
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const std::string& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier