前言
input设备的事件上报和系统中keyCode的对应是通过kl(keyLayout)文件来进行转换的。Android系统中预置了很多的kl文件,如果要定制input行为,我们也会添加或者修改kl文件。
Generic.kl部分内容
key 103 DPAD_UP
key 104 PAGE_UP
key 105 DPAD_LEFT
key 106 DPAD_RIGHT
key 107 MOVE_END
key 108 DPAD_DOWN
key 109 PAGE_DOWN
一个Android设备会存在多个input设备,本文主要分析是如何为不同的input设备寻找匹配对应的kl(keyLayout)文件的。
了解后就知道如果新增了一个输入设备,我们该如何为其添加kl文件。
一、规则
根据代码分析后得出的规则,下一节是代码的分析,不感兴趣的可以直接看这一节。
二、代码分析
1、
frameworks/native/services/inputflinger/reader/EventHub.cpp
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
device->keyMap是一个Keyboard对象
2、
/frameworks/native/libs/input/Keyboard.cpp
主要关注几个probeKeyMap方法
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.if (deviceConfiguration) {
String8 keyLayoutName;
//根据属性值来执行loadKeyLayout,但是这里实际上没有这个属性值,所以没走这个逻辑
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but ""it was not found.",
deviceIdentifier.name.c_str(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
//根据属性值来执行loadKeyCharacterMap,但是这里实际上没有这个属性值,所以没走这个逻辑
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str());
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character ""map '%s' but it was not found.",
deviceIdentifier.name.c_str(), keyCharacterMapName.string());
}
}
if (isComplete()) {
return OK;
}
}
// Try searching by device identifier.
//通过设备的identifier来匹配kl文件。名字为"",即为空。
if (probeKeyMap(deviceIdentifier, "")) {
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.
//如果按前面的规则没有找到,则使用Generic.kl文件
if (probeKeyMap(deviceIdentifier, "Generic")) {
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdentifier, "Virtual")) {
return OK;
}
// Give up!ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdentifier.name.c_str());
return NAME_NOT_FOUND;
}
1、根据Identifier信息或者设备名查找kl文件
可以通过dumusys input命令来查看设备的Identifier信息和设备名。
也可以看到所使用的kl文件,如下图使用的是gpio-keys.kl,即采用了与设备名一致的kl。
也有采用了Identifier信息来匹配kl的设备,如下:
首先probeKeyMap(deviceIdentifier, “”)根据inputDevice的Identifier信息来进行匹配。这里传入的keyMapName为""
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const std::string& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
loadKeyLayout方法中使用getPath来寻找kl文件的地址,之后通过load方法来加载解析kl文件。这里我们把重心放在寻找kl文件的地址上。
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name) {
//寻找kl文件地址的逻辑
std::string path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.empty()) {
return NAME_NOT_FOUND;
}
//加载解析kl文件
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
if (status) {
return status;
}
keyLayoutFile = path;
return OK;
}
因为前面传的name是"",所以这里执行getInputDeviceConfigurationFilePathByDeviceIdentifier,即根据Identifier信息匹配kl。
std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
const std::string& name, InputDeviceConfigurationFileType type) {
//因为之前传的name是"",所以这里会走到getInputDeviceConfigurationFilePathByDeviceIdentifier
return name.empty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
getInputDeviceConfigurationFilePathByDeviceIdentifier方法根据InputDevice的Identitier的信息来组合成name,然后通过getInputDeviceConfigurationFilePathByName方法去匹配kl文件。
- 首先匹配vendor、product、version。名称为Vendor_xxxx_Product_xxxx_Version_xxxx.kl
- 如果没有匹配到,则匹配vendor、product。名称为Vendor_xxxx_Product_xxxx.kl
- 如果还是没有匹配到,则拿设备名进行匹配。
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
//如果version不为空,则根据vendor、product、version查找对应kl文件,格式为
//Vendor_xxxx_Product_xxxx_Version_xxxx.kl
std::string versionPath = getInputDeviceConfigurationFilePathByName(
StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type);
if (!versionPath.empty()) {
return versionPath;
}
}
// Try vendor product.
//如果version为空或者前面通过version没有找到对应的kl文件,则匹配Vendor和Product去找。
std::string productPath = getInputDeviceConfigurationFilePathByName(
StringPrintf("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type);
if (!productPath.empty()) {
return productPath;
}
}
// Try device name.
//前面都匹配失败,则拿设备名进行匹配
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
}
getInputDeviceConfigurationFilePathByName方法会首先从/odm/usr/keylayout/、/vendor/usr/keylayout/和/system/usr/keylayout/文件夹下去寻找kl文件,之后再从用户目录去寻找。
std::string getInputDeviceConfigurationFilePathByName(
const std::string& name, InputDeviceConfigurationFileType type) {
// Search system repository.std::string path;
// Treblized input device config files will be located /odm/usr or /vendor/usr.
const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")};
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
}
path = rootsForPartition[i];
path += "/usr/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBEALOGD("Probing for system provided input device configuration file: path='%s'",
path.c_str());
#endifif (!access(path.c_str(), R_OK)) {
#if DEBUG_PROBEALOGD("Found");
#endifreturn path;
}
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path = "";
char *androidData = getenv("ANDROID_DATA");
if (androidData != nullptr) {
path += androidData;
}
path += "/system/devices/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
#if DEBUG_PROBEALOGD("Probing for system user input device configuration file: path='%s'", path.c_str());
#endifif (!access(path.c_str(), R_OK)) {
#if DEBUG_PROBEALOGD("Found");
#endifreturn path;
}
// Not found.
#if DEBUG_PROBEALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
name.c_str(), type);
#endifreturn "";
}
2、指定使用Generic.kl文件
如果通过Identifier或者设备名称都没有找到对应的kl,则接下来执行文章来源:https://www.toymoban.com/news/detail-813656.html
probeKeyMap(deviceIdentifier, "Generic")
即指定使用/system/usr/keylayout/Generic.kl文件作为后备keylayout。文章来源地址https://www.toymoban.com/news/detail-813656.html
到了这里,关于Android input输入设备与kl文件的匹配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!