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

这篇具有很好参考价值的文章主要介绍了Android input输入设备与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文件的匹配,android,智能手机,人机交互

二、代码分析

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文件的匹配,android,智能手机,人机交互
也有采用了Identifier信息来匹配kl的设备,如下:
Android input输入设备与kl文件的匹配,android,智能手机,人机交互

首先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。文章来源地址https://www.toymoban.com/news/detail-813656.html

到了这里,关于Android input输入设备与kl文件的匹配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 如何从 Android 设备恢复已删除的文件?

    从 Android 设备恢复已删除的文件很简单,但您需要了解内部恢复和SD 卡恢复之间的区别。 目前销售的大多数 Android 设备都配备了 SD 卡插槽(通常为 microSD),可以轻松添加额外的存储空间。该存储空间可用于存储照片、视频、文档,有时甚至是应用程序数据。但是,它不能用

    2024年02月13日
    浏览(14)
  • adb 获取 Android 设备中已安装的 apk 文件

    adb 获取 Android 设备中已安装的 apk 文件

    今天发现手机上一个应用在应用商店已经搜索不到了,想把其推荐给朋友使用,发现不知道从哪里找原始的 apk 安装文件,记录一下。 两种方法 可以使用 MT管理器 ( Android 平台逆向神器 ),它有个 安装包提取 的功能,可以方便快捷的查看应用包名及导出。MT管理器官方下载地

    2024年02月04日
    浏览(16)
  • Android中system/bin/Input命令 -- Android12

    Android中system/bin/Input命令 -- Android12

    android12-release ANdroid12之前可查看一下 IMS:injectInputEvent注入Input事件,Android12更新相关代码: frameworks/base/services/core/java/com/android/server/input/InputManagerService.java frameworks/base/services/core/java/com/android/server/input/InputShellCommand.java frameworks/base/core/java/android/os/ShellCommand.java frameworks/libs/m

    2024年02月13日
    浏览(10)
  • [Android 13]Input系列--EventHub获取事件

    hongxi.zhu 2023-7-12 Android T 从前面inputflinger的启动分析中,我们知道事件来源是在 EventHub::getEvents , 所以我们重点看下这个方法的流程来了解事件是如何从驱动上报中获取的。 EventHub::getEvents frameworks/native/services/inputflinger/reader/EventHub.cpp EventHub::scanDevicesLocked() EventHub::openDeviceLock

    2024年02月15日
    浏览(15)
  • [Android 13]Input系列--InputFlinger的启动

    hongxi.zhu 2023-7-11 Android T frameworks/base/services/java/com/android/server/SystemServer.java 获取InputManagerService,并走初始化inputManager(java层和native层)流程 WindowManagerService服务构造时传入inputManager对象,用户wms和ims交互 向ServiceManager注册java层ims服务 启动inputflinger,处理输入事件 我们重点看

    2024年02月13日
    浏览(12)
  • Android13 adb input 调试命令使用和源码解析

    Android13 adb input 调试命令使用和源码解析

    目录 一、概述 二、常用命令 2.1 输入文本 2.2 模拟按键 2.3 模拟点击 2.4 模拟滑动 2.5 模拟长按 2.6 模拟滚动 三、进阶用法 3.1 组合按键的用法 3.2 长按power键 3.3 输入中文 3.4 代码模拟发送按键 1. 方法1: 2. 方法2: 3. 方法3: 四、注意事项 五、源码解析 六、总结 好了 本篇作为And

    2024年01月20日
    浏览(19)
  • 【Android】ADB无线连接Android设备

    【Android】ADB无线连接Android设备

    Android Debug Bridge ,简称 adb ,是一种功能多样的命令行工具,可让您与设备进行通信。adb 命令可用于执行各种设备操作(例如安装和调试应用),并提供对 Unix shell(可用来在设备上运行各种命令)的访问权限。它是一种客户端-服务器程序,包括以下三个组件: 客户端 :用

    2023年04月08日
    浏览(26)
  • [Android 13]Input系列--触摸事件在应用进程的分发和处理

    [Android 13]Input系列--触摸事件在应用进程的分发和处理

    hongxi.zhu 2023-7-21 Android 13 前面我们已经梳理了input事件在native层的传递,这一篇我们接着探索input事件在应用中的传递与处理,我们将按键事件和触摸事件分开梳理,这一篇就只涉及触摸事件。 一、事件的接收 从前面的篇幅我们知道,framework native层 InputDispatcher 向应用通过s

    2024年02月15日
    浏览(15)
  • Android WebView 获取html页面聚焦input在页面的位置

    Android WebView 获取html页面聚焦input在页面的位置,实现代码如下: 注意: WebView 要设置setJavaScriptEnabled支持JavaScript调用 返回的数值是以dp为单位,使用时需要转换为像素px

    2024年02月15日
    浏览(12)
  • Android设备序列号:如何获取和查找您的Android设备的序列号

    Android设备序列号:如何获取和查找您的Android设备的序列号 在使用Android设备时,有时候需要查找设备的序列号来进行诊断、维修或者进行其他操作。本文将介绍如何获取和查找Android设备的序列号,并提供相应的源代码示例。 Android设备的序列号是一个唯一的标识符,用于区分

    2024年02月07日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包