Android .kl按键布局文件

news/2024/9/15 8:36:46/ 标签: android, java, 开发语言

1.介绍

一个硬件按键的处理流程大致为:当用户按下或释放一个键时,键盘硬件会生成一个扫描码scan code,然后操作系统读取这个scan code,并将scan code扫描码映射到虚拟键码key code,最后操作系统根据映射的keycode生成输入事件,并将这些事件传递给应用程序或系统服务,进而执行相应的操作。

scan code和key code通过Key layout file 映射,Key layout file一般用于定义物理键盘上各个键的功能和映射关系。文件通常以 .kl 为后缀,例如我们熟知的 Generic.kl文件。

2.scan code和key code

先来介绍下scan code和key code:

scan code:扫描码,用于表示物理键盘或其他输入设备上按键的唯一标识符。scan code是硬件级别的编码,用于告诉操作系统哪个键被按下。

key code:键码,是操作系统层面上用于识别和处理输入事件的编码,可以将硬件层面的按键映射到更高层次的抽象,使得应用程序能够处理输入事件而不必直接关心底层硬件的细节。key code定义在KeyEvent.java中,一些常见的按键:

java">    public static final int KEYCODE_DPAD_UP         = 19;//方向上键的keycode为19public static final int KEYCODE_DPAD_DOWN       = 20;//方向下键public static final int KEYCODE_DPAD_LEFT       = 21;//方向左键public static final int KEYCODE_DPAD_RIGHT      = 22;//方向右键public static final int KEYCODE_DPAD_CENTER     = 23;//中间键

3.按键声明

按键映射关系声明在kl文件内,下面以一些常见的按键声明来说明它的语法规则:

java">frameworks/base/data/keyboards/Generic.klkey 103   DPAD_UP
key 104   PAGE_UP
key 105   DPAD_LEFT
key 106   DPAD_RIGHT
key 465   ESCAPE            FUNCTION

按键声明包含关键字 key(后跟一个 Linux 按键代码编号和 Android 按键代码名称),任何一项声明都可以后跟一组由空格分隔的可选 flag。

常见的 flag:

FUNCTION:按键应解读为如同也按下了 FUNCTION 键。
GESTURE:按键由用户手势(例如手掌摸触摸屏)生成。
VIRTUAL:按键是与主触摸屏相邻的虚拟软键(电容式按钮)。这会导致启用特殊的去抖动逻辑

上述例子表明:scan code为103映射KeyEvent上的KEYCODE_DPAD_UP(后面以此类推)。

4.位置

按键布局文件由 USB vendor、product(可能还包括version)ID 或输入设备名称来确定位置。系统会按顺序查阅以下路径:

java">/odm/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/odm/usr/keylayout/DEVICE_NAME.kl
/vendor/usr/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/odm/usr/keylayout/Generic.kl
/vendor/usr/keylayout/Generic.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl

5.Generic.kl

系统提供了一个常规按键布局文件,名为 Generic.kl。此按键布局旨在支持各种标准外部键盘和操纵杆,一般情况下不要轻易修改这个文件。

6.自定义kl文件

Android支持为硬件设备自定义kl文件,kl文件的定制需要遵循一定的规则,以帮助系统和开发者区分不同的硬件设备或输入设备。

命名规则:

命名规则通常包括硬件的制造商(Vendor)、设备型号(Product)以及布局类型等信息,格式通常为:

java">Vendor_XXX_Product_XXX.kl

Vendor:代表设备的制造商或供应商。通常是一个厂商的标识符或名称的缩写。
Product:代表具体的硬件产品型号。这个部分通常与设备的具体型号相关联,帮助区分不同的硬件设备。例如:

某个厂商制造了一个键盘设备,厂商ID为 abcd,产品ID为 efgh,则键盘布局文件的命名可能是:
Vendor_abcd_Product_efgh.kl

除了基本的 Vendor、Product 形式外,一些设备可能会使用其他的命名约定来包含更多的信息,例如:

Vendor_1234_Product_5678_Layout1.kl:可能表示设备 5678 的布局1。
Vendor_1234_Product_5678_Pro.kl:可能表示设备 5678 的专业版布局。

目录和位置:
这些 .kl 文件通常存放在Android设备的系统目录中,例如 /system/usr/keylayout/。系统会根据这些文件的定义来正确处理与物理键盘相关的输入事件。

文件内容:
.kl 文件的内容包括键位定义和映射,和Generic.kl语法一致

如果没有可用的设备专属kl文件,则系统将使用默认Generic.kl文件。

7.加载流程

kl文件的加载流程主要是在EventHub.cpp里的openDeviceLocked()方法里进行的

frameworks/native/services/inputflinger/reader/EventHub.cpp
void EventHub::openDeviceLocked(const std::string& devicePath) {......// Load the key map.// We need to do this for joysticks too because the key layout may specify axes, and for// sensor as well because the key layout may specify the axes to sensor data mapping.status_t keyMapStatus = NAME_NOT_FOUND;if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |InputDeviceClass::SENSOR)) {// Load the keymap for the device.keyMapStatus = device->loadKeyMapLocked();}......
}

调用loadKeyMapLocked()方法继续加载

frameworks/native/services/inputflinger/reader/EventHub.cpp
status_t EventHub::Device::loadKeyMapLocked() {return keyMap.load(identifier, configuration.get());
}

 调用frameworks/native/libs/input/Keyboard.cpp里的load方法,用来加载和配置键盘映射

frameworks/native/libs/input/Keyboard.cpp
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier,const PropertyMap* deviceConfiguration) {// Use the configured key layout if available.if (deviceConfiguration) {//如果.idc文件里有指定使用的kl文件,则首先加载指定的文件std::optional<std::string> keyLayoutName =deviceConfiguration->getString("keyboard.layout");//根据keyboard.layout字段来获取指定的.kl文件if (keyLayoutName.has_value()) {//如果存在指定的kl文件status_t status = loadKeyLayout(deviceIdentifier, *keyLayoutName);//加载键盘布局if (status == NAME_NOT_FOUND) {//未找到对应名称的kl文件ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but ""it was not found.",deviceIdentifier.name.c_str(), keyLayoutName->c_str());}}std::optional<std::string> keyCharacterMapName =deviceConfiguration->getString("keyboard.characterMap");//根据keyboard.characterMap字段来获取字符映射表的名称,一般是以.kcm结尾的文件if (keyCharacterMapName.has_value()) {status_t status = loadKeyCharacterMap(deviceIdentifier, *keyCharacterMapName);//加载kcm文件。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->c_str());}}if (isComplete()) {//加载完成之后返回return OK;}}// Try searching by device identifier.if (probeKeyMap(deviceIdentifier, "")) {//通过设备标识符查找键盘映射,即查找对应目录下是否有自定义的kl文件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(deviceIdentifier, "Generic")) {//如果没有找到合适的kl文件,返回Generic.kl文件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;//以上方式都找不到kl文件,查找失败
}

可以看出kl文件的加载遵循以下顺序:

(1)如果.idc文件里keyboard.layout何keyboard.characterMap有指定使用的kl、kcm文件,则首先加载指定的kl、kcm文件

(2)如果.idc文件里没有配置指定的kl文件,那么通过设备信息去查找kl文件,一般为Vendor_XXX_Product_XXX(_Version_XXX).kl文件

(3)如果上述两种情况都没有找到kl文件,那么加载默认的Generic.kl文件

(4)最后尝试加载虚拟键盘映射表


http://www.ppmy.cn/news/1506781.html

相关文章

Vue的事件处理、事件修饰符、键盘事件

目录 1. 事件处理基本使用2. 事件修饰符3. 键盘事件 1. 事件处理基本使用 使用v-on:xxx或xxx绑定事件&#xff0c;其中xxx是事件名&#xff0c;比如clickmethods中配置的函数&#xff0c;都是被Vue所管理的函数&#xff0c;this的指向是vm或组件实例对象 <!DOCTYPE html&g…

Redis 分布式锁实现详解

Redis 分布式锁实现详解 在分布式系统中&#xff0c;我们需要解决的一个重要问题是多个服务实例之间如何协调共享资源的访问问题。例如&#xff0c;在电子商务系统中&#xff0c;库存更新需要被多个微服务实例所共享&#xff0c;但为了防止超卖&#xff0c;必须确保库存更新是…

复现一下最近学习的漏洞(sqlab 1-10)

第一个问题&#xff1a;为什么不能用#来闭合单引号呢&#xff1f; 在进行URL地址栏传参的时候&#xff0c;是有一套编码规范的。他不会编码英文、数字和某些符号。但是#它会进行编码。也就是%23。&#xff08;先转ascii码&#xff0c;然后再转十六进制&#xff0c;之后加上%就是…

20240810设置倒立字图片的方法

20240810设置倒立字图片的方法 2024/8/10 20:38 缘起&#xff1a;有些时候&#xff0c;为了/需要达到某些效果。需要将字倒立。 比较简单的方法&#xff1a; 百度&#xff1a;在线 倒着写 WPS 倒立文字 1、最简单粗暴的。在记事本/WPS中输入文字之后&#xff0c;使用微信/QQ截…

编程-设计模式 2:抽象工厂模式

设计模式 2&#xff1a;抽象工厂模式 定义与目的 定义&#xff1a;抽象工厂模式提供一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。目的&#xff1a;该模式的主要目的是解耦客户端代码与产品类之间的关系&#xff0c;并确保一…

【Linux:进程优先级】

什么叫做优先级&#xff1a; 指进程获取某种资源的顺序 在linux中&#xff0c;进程是以struck_task进行描述的&#xff0c;他的本质就是一个结构体&#xff0c;该结构体中含有很多个内部字段&#xff0c;优先级就属于该结构体中的某个字段。此外&#xff0c;在Linux环境下&am…

新160个crackme - 027-MexeliteCRK1

运行分析 需破解Serial和Status PE分析 Delphi程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索字符串&#xff0c;找到关键字符串 先静态分析&#xff0c;结果如上注释 比较Serial和’Benadryl’字符串&#xff0c;相等弹出成功 验证成功

第二十二天学习笔记2024.8.6

同步时间 yum -y install ntpdate.x86_64 ntpdate ntp.ntsc.ac.cn 两台主机都要安装 libaio 和rsync yum -y install rsync rpm -qa |grep libaio查看是否安装 没有就安装 然后安装mysql8.0的安装包解压 [rootmsater ~]# tar -xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar …

服务器CPU架构有几种?分别应用到什么场景?有啥优缺点?

服务器CPU架构主要有以下几种&#xff1a; 1. x86 架构 应用场景&#xff1a; 企业数据中心&#xff1a;广泛用于企业级服务器&#xff0c;如Web服务器、数据库服务器、虚拟化服务器等。高性能计算&#xff08;HPC&#xff09;&#xff1a;用于科学计算、工程模拟等需要强大计…

token续签方案

在处理 Token 的有效期续签时&#xff0c;通常有两种主要策略&#xff1a;自动续签和手动续签。以下是实现这些策略的一些建议&#xff1a; 1. 自动续签 这种方法通常通过使用刷新 Token 来实现&#xff1a; 颁发两个 Token&#xff1a; 访问 Token&#xff08;Access Token…

Ubuntu22.04安装Go语言的几种方式

在 Ubuntu 22.04 上安装 Go 语言可以通过几种不同的方法&#xff0c;以下是两种常见的安装方法&#xff1a; 方法1&#xff1a;使用 go 官方安装脚本 打开终端。 下载 Go 语言的安装脚本&#xff1a; curl -O https://go.dev/dl/go1.22.5.linux-amd64.tar.gz请检查 Go 官方网…

为什么有些时候prefetch下载不只一个sra文件

一些SRA文件使用prefetch的时候,会下载多个文件,例如SRR11579188 $ ls CM000663.2 CM000666.2 CM000669.2 CM000672.2 CM000675.2 CM000678.2 CM000681.2 CM000684.2 J01415.2 CM000664.2 CM000667.2 CM000670.2 CM000673.2 CM000676.2 CM000679.2 CM000682.2…

Spring Validation 校验 ( 一 )

Spring Validation 是 Spring Framework 的一部分&#xff0c;它提供了一种简单的方式来验证 Java 对象的数据。Spring Validation 基于 JSR 303/JSR 349&#xff08;也称为 Bean Validation&#xff09;规范&#xff0c;允许开发者使用注解来定义对象的约束条件&#xff0c;从…

Java JVM中的栈空间怎么释放

在Java虚拟机 (JVM) 中&#xff0c;栈空间主要用于存储方法调用时的信息&#xff0c;例如局部变量、操作数栈、动态链接信息以及返回地址等。当一个方法被调用时&#xff0c;一个新的栈帧会在当前线程的栈中被创建&#xff1b;当该方法执行完毕后&#xff0c;这个栈帧就会被销毁…

【Python】Django Web 框架

一、常用的Web开发框架 1.Django Django是一个由Python写成的开放源代码的Web应用框架。这套框架的主要目标是使开发复杂、数据库驱动的网站变得简单。Django注重组件的重用性和“可拔插性”、敏捷开发和DRY(Dont Repeat Yourself)法则 2.Flask Flask是一个微型的Python开发…

DevExpress WPF中文教程:如何在GridControl中对数据排序、分组、过滤?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

java控制台进阶知识

Java 控制台&#xff08;通常指的是标准输入 System.in 和标准输出 System.out&#xff09;是进行命令行交互的基本工具。对于基本的输入输出操作&#xff0c;Java 提供了 Scanner 类和 PrintStream 类等。但是&#xff0c;如果你想要实现更高级的功能&#xff0c;例如颜色输出…

SaaS 系统的详细讲解

SaaS 系统的详细讲解 目录 概述SaaS 的基本概念 2.1 什么是 SaaS2.2 SaaS 的优势2.3 SaaS 的挑战SaaS 架构设计 3.1 多租户架构3.2 数据隔离与安全3.3 可扩展性设计3.4 配置化与自定义SaaS 系统的关键组件 4.1 用户管理与认证4.2 计费与订阅管理4.3 数据存储与访问4.4 API 网关…

茶余饭后(四)

1&#xff0c;越来越安静 2&#xff0c;有计划&#xff0c;勤反思 3&#xff0c;骨子里不相信任何人 4&#xff0c;对未来有明确地目标 5&#xff0c;不再流泪&#xff0c;什么事都愿意自己去做 6&#xff0c;学会孤独&#xff0c;养成独立人格 7&#xff0c;做自己喜欢的…

【数据结构七夕专属版】单链表及单链表的实现【附源码和源码讲解】

本篇是博主在学习数据结构时的心得&#xff0c;希望能够帮助到大家&#xff0c;也许有些许遗漏&#xff0c;但博主已经尽了最大努力打破信息差&#xff0c;如果有遗漏还请见谅&#xff0c;嘻嘻&#xff0c;前路漫漫&#xff0c;我们一起前进&#xff01;&#xff01;&#xff0…