Android input输入设备与kl文件的匹配

文章目录

  • 前言
  • 一、规则
  • 二、代码分析
    • 1、根据Identifier信息或者设备名查找kl文件
    • 2、指定使用Generic.kl文件


前言

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文件。

一、规则

根据代码分析后得出的规则,下一节是代码的分析,不感兴趣的可以直接看这一节。
Android input输入设备与kl文件的匹配_第1张图片

二、代码分析

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。
Android input输入设备与kl文件的匹配_第2张图片
也有采用了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文件。

  1. 首先匹配vendor、product、version。名称为Vendor_xxxx_Product_xxxx_Version_xxxx.kl
  2. 如果没有匹配到,则匹配vendor、product。名称为Vendor_xxxx_Product_xxxx.kl
  3. 如果还是没有匹配到,则拿设备名进行匹配。
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,则接下来执行

probeKeyMap(deviceIdentifier, "Generic")

即指定使用/system/usr/keylayout/Generic.kl文件作为后备keylayout。

你可能感兴趣的:(android,智能手机,人机交互)