Android6.0 按键kl文件加载过程分析

news/2024/11/29 18:40:46/

在之前按键过程分析的几篇博客中,我分析过关于按键kl文件的加载,但是讲的不是非常详细,这篇博客主要把kl文件加载过程单独拉出来分析下。

 

1. 获取InputDeviceIdentifier的name 以及 Device的创建

InputDeviceIdentifier的name 非常重要,后面寻找idc kl kcm文件都需要这个name。

我们看下面的调用流程EventHub::getEvents -> EventHub::scanDevicesLocked -> EventHub::scanDirLocked -> EventHub::openDeviceLocked

我们来看EventHub::openDeviceLocked函数,先是打开devicePath,然后利用ioctl获取InputDeviceIdentifier的name

status_t EventHub::openDeviceLocked(const char *devicePath) {char buffer[80];ALOGE("Opening device: %s", devicePath);int fd = open(devicePath, O_RDWR | O_CLOEXEC);if(fd < 0) {ALOGE("could not open %s, %s\n", devicePath, strerror(errno));return -1;}InputDeviceIdentifier identifier;// Get device name.if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {//fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));} else {buffer[sizeof(buffer) - 1] = '\0';identifier.name.setTo(buffer);}

下面创建了Device,各种打印。

    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);ALOGW("add device %d: %s\n", deviceId, devicePath); ALOGW("  bus:        %04x\n""  vendor      %04x\n""  product     %04x\n""  version     %04x\n",identifier.bus, identifier.vendor, identifier.product, identifier.version);ALOGW("  name:       \"%s\"\n", identifier.name.string());ALOGW("  location:   \"%s\"\n", identifier.location.string());ALOGW("  unique id:  \"%s\"\n", identifier.uniqueId.string());ALOGW("  descriptor: \"%s\"\n", identifier.descriptor.string());ALOGW("  driver:     v%d.%d.%d\n",driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);// Load the configuration file for the device.loadConfigurationLocked(device);

先来看下打印

EventHub: Opening device: /dev/input/event4
EventHub: Created descriptor: raw=:0000:0000:name:comip_snd_soc Headset, cooked=2efc90e2a7d3beb2de2b795a507e8489f0acd57f
EventHub: add device 1: /dev/input/event4
EventHub:   bus:        0000
EventHub:   vendor      0000
EventHub:   product     0000
EventHub:   version     0000
EventHub:   name:       "comip_snd_soc Headset"
EventHub:   location:   "ALSA"
EventHub:   unique id:  ""
EventHub:   descriptor: "2efc90e2a7d3beb2de2b795a507e8489f0acd57f"
EventHub:   driver:     v1.0.1

 

2. 加载idc文件

在我们的设备中,一般没有定义自己的idc文件,也就找不到。一般定义idc文件,是在这个文件中定义kl 和kcm文件。

我们再来分析下loadConfigurationLocked函数,调用getInputDeviceConfigurationFilePathByDeviceIdentifier函数,当configurationFile不为空的时候,调用PropertyMap::load加载idc文件,这部分代码是在system/libutil下面的,当有这个idc文件的时候,device->configuration就不为空。

void EventHub::loadConfigurationLocked(Device* device) {device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);if (device->configurationFile.isEmpty()) {//configurationFile为空ALOGD("No input device configuration file found for device '%s'.",device->identifier.name.string());} else {//如果有configurationFile文件,那我们就调用PropertyMap::load函数ALOGD("input device configuration file name '%s'.",device->configurationFile.string());status_t status = PropertyMap::load(device->configurationFile,&device->configuration);if (status) {ALOGE("Error loading input device configuration file for device '%s'.  ""Using default configuration.",device->identifier.name.string());}}
}

调用了getInputDeviceConfigurationFilePathByDeviceIdentifier函数,其中type为0,代表是idc文件

String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(const InputDeviceIdentifier& deviceIdentifier,InputDeviceConfigurationFileType type) {if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {//不进入这个分支if (deviceIdentifier.version != 0) {// Try vendor product version.String8 versionPath(getInputDeviceConfigurationFilePathByName(String8::format("Vendor_%04x_Product_%04x_Version_%04x",deviceIdentifier.vendor, deviceIdentifier.product,deviceIdentifier.version),type));if (!versionPath.isEmpty()) {return versionPath;}}// Try vendor product.String8 productPath(getInputDeviceConfigurationFilePathByName(String8::format("Vendor_%04x_Product_%04x",deviceIdentifier.vendor, deviceIdentifier.product),type));if (!productPath.isEmpty()) {return productPath;}}// Try device name.return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}

于是我们再来看getInputDeviceConfigurationFilePathByName函数:

String8 getInputDeviceConfigurationFilePathByName(const String8& name, InputDeviceConfigurationFileType type) {// Search system repository.String8 path;path.setTo(getenv("ANDROID_ROOT"));path.append("/usr/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Search user repository.// TODO Should only look here if not in safe mode.path.setTo(getenv("ANDROID_DATA"));path.append("/system/devices/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system user input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Not found.ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",name.string(), type);return String8();
}

这个函数就是寻找各种匹配的idc文件,最后没找到就返回一个空的String。我们来看下appendInputDeviceConfigurationFileRelativePath函数

static void appendInputDeviceConfigurationFileRelativePath(String8& path,const String8& name, InputDeviceConfigurationFileType type) {path.append(CONFIGURATION_FILE_DIR[type]);for (size_t i = 0; i < name.length(); i++) {char ch = name[i];if (!isValidNameChar(ch)) {ch = '_';}path.append(&ch, 1);}path.append(CONFIGURATION_FILE_EXTENSION[type]);
}
static const char* CONFIGURATION_FILE_DIR[] = {"idc/","keylayout/","keychars/",
};static const char* CONFIGURATION_FILE_EXTENSION[] = {".idc",".kl",".kcm",
};

这个函数就是用传进来的路径和名字,组成一个idc文件。然后在getInputDeviceConfigurationFilePathByName文件中看用appendInputDeviceConfigurationFileRelativePath文件组成的idc文件是否有这个文件,有那就找到了返回idc的文件路径,如果没有最后返回一个空的string。我们看我们的这段log。

InputDevice: Probing for system provided input device configuration file: path='/system/usr/idc/comip_snd_soc_Headset.idc'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/idc/comip_snd_soc_Headset.idc'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=0
EventHub: No input device configuration file found for device 'comip_snd_soc Headset'.

这段log说明没有这样的idc文件。

root@lte26007:/system/usr/idc # ls
AVRCP.idc
qwerty.idc
qwerty2.idc

我们看我们手机上的idc目录,都是原生的,也就是framework/base/data下面的文件,都是原生的也就肯定找不到匹配的idc文件。idc文件中保存这kl kcm文件的名字。

下面是qwerty.idc文件,下面是它的内容,keyboard.layout代表kl的文件名,keyboard.characterMap代表kcm的文件名。

 

touch.deviceType = touchScreen
touch.orientationAware = 1keyboard.layout = qwerty
keyboard.characterMap = qwerty
keyboard.orientationAware = 1
keyboard.builtIn = 1cursor.mode = navigation
cursor.orientationAware = 1

 

 

 

 

3. 加载kl文件

加载kl 和 kcm文件是在openDeviceLocked函数中调用loadKeyMapLocked函数完成的。

那我们继续分析openDeviceLocked函数,关于加载kl文件的那部分代码:

 

    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);}

loadKeyMapLocked函数

 

 

status_t EventHub::loadKeyMapLocked(Device* device) {return device->keyMap.load(device->identifier, device->configuration);
}

我们再来看load函数,前面我们的idc文件没有找到匹配的,因此第一个分支可以直接跳过,可以直接看probeKeyMap函数。

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);if (status == NAME_NOT_FOUND) {ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but ""it was not found.",deviceIdenfifier.name.string(), keyLayoutName.string());}}String8 keyCharacterMapName;if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),keyCharacterMapName)) {status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);if (status == NAME_NOT_FOUND) {ALOGE("Configuration for keyboard device '%s' requested keyboard character ""map '%s' but it was not found.",deviceIdenfifier.name.string(), keyLayoutName.string());}}if (isComplete()) {return OK;}}// Try searching by device identifier.if (probeKeyMap(deviceIdenfifier, String8::empty())) {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, String8("Generic"))) {return OK;}// Try the Virtual key map as a last resort.if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {return OK;}// Give up!ALOGE("Could not determine key map for device '%s' and no default key maps were found!",deviceIdenfifier.name.string());return NAME_NOT_FOUND;
}

 

3.1 没有找到匹配InputDeviceIdentifier的name的kl文件 使用原生的Generic.kl文件

第一种情况是没有找到匹配InputDeviceIdentifier的name的kl文件,这个时候我们一般就用Generic.kl文件代替。

下面我们直接看probeKeyMap函数:

bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,const String8& keyMapName) {if (!haveKeyLayout()) {//是否有kl文件loadKeyLayout(deviceIdentifier, keyMapName);}if (!haveKeyCharacterMap()) {//是否有kcm文件loadKeyCharacterMap(deviceIdentifier, keyMapName);}return isComplete();
}

先来看下isComplete函数,kl文件和kcm文件都有了才返回true,看load函数,当isComplete返回true,就直接return了,因为kl 和 kcm文件都找到了。

    inline bool isComplete() const {return haveKeyLayout() && haveKeyCharacterMap();}

下面我们看下加载kl文件的过程,kcm文件的加载过程和kl类似我们就不看了。

status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,const String8& name) {String8 path(getPath(deviceIdentifier, name,INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));if (path.isEmpty()) {return NAME_NOT_FOUND;}ALOGE("loadKeyLayout path '%s'.",path.string());status_t status = KeyLayoutMap::load(path, &keyLayoutMap);if (status) {return status;}keyLayoutFile.setTo(path);return OK;
}

先看下getPath函数,第一个在load函数中调用loadKeyLayout的name是空的,所以这里就是用了getInputDeviceConfigurationFilePathByDeviceIdentifier函数。

String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,const String8& name, InputDeviceConfigurationFileType type) {return name.isEmpty()? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type): getInputDeviceConfigurationFilePathByName(name, type);
}

看下getInputDeviceConfigurationFilePathByDeviceIdentifier函数,第一部分就是各种Vendor之类的kl,我们没有走进这个分支。

String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(const InputDeviceIdentifier& deviceIdentifier,InputDeviceConfigurationFileType type) {if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {if (deviceIdentifier.version != 0) {// Try vendor product version.String8 versionPath(getInputDeviceConfigurationFilePathByName(String8::format("Vendor_%04x_Product_%04x_Version_%04x",deviceIdentifier.vendor, deviceIdentifier.product,deviceIdentifier.version),type));if (!versionPath.isEmpty()) {return versionPath;}}// Try vendor product.String8 productPath(getInputDeviceConfigurationFilePathByName(String8::format("Vendor_%04x_Product_%04x",deviceIdentifier.vendor, deviceIdentifier.product),type));if (!productPath.isEmpty()) {return productPath;}}// Try device name.return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}

因此我们直接看下getInputDeviceConfigurationFilePathByName函数,和之前找idc那个函数一样,只是这里是用来找kl文件了

String8 getInputDeviceConfigurationFilePathByName(const String8& name, InputDeviceConfigurationFileType type) {// Search system repository.String8 path;path.setTo(getenv("ANDROID_ROOT"));path.append("/usr/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Search user repository.// TODO Should only look here if not in safe mode.path.setTo(getenv("ANDROID_DATA"));path.append("/system/devices/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system user input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Not found.ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",name.string(), type);return String8();
}

之前我们的log中,这个name就是comip_snd_soc Headset,也没有找到。

EventHub:   name:       "comip_snd_soc Headset"

我们看下设备有哪些kl文件,确实没有comip_snd_soc Headset这个文件。

AVRCP.kl
Generic.kl
Vendor_0079_Product_0011.kl
Vendor_045e_Product_028e.kl
Vendor_046d_Product_b501.kl
Vendor_046d_Product_c216.kl
Vendor_046d_Product_c219.kl
Vendor_046d_Product_c21d.kl
Vendor_046d_Product_c21f.kl
Vendor_046d_Product_c294.kl
Vendor_046d_Product_c299.kl
Vendor_046d_Product_c532.kl
Vendor_054c_Product_0268.kl
Vendor_0583_Product_2060.kl
Vendor_05ac_Product_0239.kl
Vendor_0b05_Product_4500.kl
Vendor_1038_Product_1412.kl
Vendor_12bd_Product_d015.kl
Vendor_1532_Product_0900.kl
Vendor_1689_Product_fd00.kl
Vendor_1689_Product_fd01.kl
Vendor_1689_Product_fe00.kl
Vendor_18d1_Product_2c40.kl
Vendor_18d1_Product_5018.kl
Vendor_1949_Product_0401.kl
Vendor_1bad_Product_f016.kl
Vendor_1bad_Product_f023.kl
Vendor_1bad_Product_f027.kl
Vendor_1bad_Product_f036.kl
Vendor_1d79_Product_0009.kl
Vendor_22b8_Product_093d.kl
Vendor_2378_Product_1008.kl
Vendor_2378_Product_100a.kl
comip-gpio-keys.kl
comip-keypad.kl
ft5x06.kl
h2w_headset.kl
qwerty.kl
sensor00fn11.kl

我们回过头在来看load函数调用的第二个probeKeyMap函数,是传入了Generic参数,

    if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {return OK;}

我们再来看看probeKeyMap函数,还是调用了loadKeyLayout函数

bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,const String8& keyMapName) {if (!haveKeyLayout()) {loadKeyLayout(deviceIdentifier, keyMapName);}if (!haveKeyCharacterMap()) {loadKeyCharacterMap(deviceIdentifier, keyMapName);}return isComplete();
}status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,const String8& name) {String8 path(getPath(deviceIdentifier, name,INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));if (path.isEmpty()) {return NAME_NOT_FOUND;}ALOGE("kangchen  loadKeyLayout path '%s'.",path.string());status_t status = KeyLayoutMap::load(path, &keyLayoutMap);if (status) {return status;}keyLayoutFile.setTo(path);return OK;
}

同样我们来看getPath函数,这个时候name不是空了,就调用getInputDeviceConfigurationFilePathByName函数

String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,const String8& name, InputDeviceConfigurationFileType type) {return name.isEmpty()? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type): getInputDeviceConfigurationFilePathByName(name, type);
}

getInputDeviceConfigurationFilePathByName函数,最后就在这个函数中找到了Generic.kl文件。

String8 getInputDeviceConfigurationFilePathByName(const String8& name, InputDeviceConfigurationFileType type) {// Search system repository.String8 path;path.setTo(getenv("ANDROID_ROOT"));path.append("/usr/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Search user repository.// TODO Should only look here if not in safe mode.path.setTo(getenv("ANDROID_DATA"));path.append("/system/devices/");appendInputDeviceConfigurationFileRelativePath(path, name, type);ALOGD("Probing for system user input device configuration file: path='%s'", path.string());if (!access(path.string(), R_OK)) {ALOGD("Found");return path;}// Not found.ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",name.string(), type);return String8();
}

找到kl文件后,我们会对这个文件在load函数中进行解析,这个我们就不分析了,就是把扫描码转换成按键码之类。

status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {outMap->clear();Tokenizer* tokenizer;status_t status = Tokenizer::open(filename, &tokenizer);if (status) {ALOGE("Error %d opening key layout map file %s.", status, filename.string());} else {sp<KeyLayoutMap> map = new KeyLayoutMap();if (!map.get()) {ALOGE("Error allocating key layout map.");status = NO_MEMORY;} else {
#if DEBUG_PARSER_PERFORMANCEnsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endifParser parser(map.get(), tokenizer);status = parser.parse();
#if DEBUG_PARSER_PERFORMANCEnsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",tokenizer->getFilename().string(), tokenizer->getLineNumber(),elapsedTime / 1000000.0);
#endifif (!status) {*outMap = map;}}delete tokenizer;}return status;
}

下面的数字代表扫描码,而旁边的代码键值码。

 

key 113   VOLUME_MUTE
key 114   VOLUME_DOWN
key 115   VOLUME_UP
key 116   POWER

这样就完成了kl文件的加载解析。我们看下这段的log

 

 

InputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/comip_snd_soc_Headset.kl'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keylayout/comip_snd_soc_Headset.kl'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=1
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/comip_snd_soc_Headset.kcm'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keychars/comip_snd_soc_Headset.kcm'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=2
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/Generic.kl'
InputDevice: Found
Keyboard: loadKeyLayout path '/system/usr/keylayout/Generic.kl'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/Generic.kcm'
InputDevice: Found
EventHub: Unable to disable kernel key repeat for /dev/input/event4: Function not implemented
EventHub: New device: id=1, fd=70, path='/dev/input/event4', name='comip_snd_soc Headset', classes=0x81, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true

上面的例子是加载了Generic.kl文件,是因为在system/usr/keylayout下面没有找到合适的。

 

3.2 找到匹配InputDeviceIdentifier的name的kl文件

下面我们的例子是找到合适的kl文件的,我们来看下log,注意name是ft5x06

EventHub: Opening device: /dev/input/event3
EventHub: Created descriptor: raw=:0000:0000:name:ft5x06, cooked=f2706364e2849110ed562db0c53423b5027a6cc5
EventHub: add device 2: /dev/input/event3
EventHub:   bus:        0000
EventHub:   vendor      0000
EventHub:   product     0000
EventHub:   version     0000
EventHub:   name:       "ft5x06"
EventHub:   location:   ""
EventHub:   unique id:  ""
EventHub:   descriptor: "f2706364e2849110ed562db0c53423b5027a6cc5"
EventHub:   driver:     v1.0.1
InputDevice: Probing for system provided input device configuration file: path='/system/usr/idc/ft5x06.idc'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/idc/ft5x06.idc'
InputDevice: Probe failed to find input device configuration file: name='ft5x06', type=0
EventHub: No input device configuration file found for device 'ft5x06'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/ft5x06.kl'
InputDevice: Found
Keyboard: loadKeyLayout path '/system/usr/keylayout/ft5x06.kl'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/ft5x06.kcm'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keychars/ft5x06.kcm'
InputDevice: Probe failed to find input device configuration file: name='ft5x06', type=2
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/Generic.kcm'
InputDevice: Found
EventHub: Unable to disable kernel key repeat for /dev/input/event3: Function not implemented
EventHub: New device: id=2, fd=71, path='/dev/input/event3', name='ft5x06', classes=0x15, configuration='', keyLayout='/system/usr/keylayout/ft5x06.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true

我们再来看设备中的kl文件,有ft5x06.kl文件,这样就找到了匹配的kl文件,而不用原生的Generic.kl了。

AVRCP.kl
Generic.kl
Vendor_0079_Product_0011.kl
Vendor_045e_Product_028e.kl
Vendor_046d_Product_b501.kl
Vendor_046d_Product_c216.kl
Vendor_046d_Product_c219.kl
Vendor_046d_Product_c21d.kl
Vendor_046d_Product_c21f.kl
Vendor_046d_Product_c294.kl
Vendor_046d_Product_c299.kl
Vendor_046d_Product_c532.kl
Vendor_054c_Product_0268.kl
Vendor_0583_Product_2060.kl
Vendor_05ac_Product_0239.kl
Vendor_0b05_Product_4500.kl
Vendor_1038_Product_1412.kl
Vendor_12bd_Product_d015.kl
Vendor_1532_Product_0900.kl
Vendor_1689_Product_fd00.kl
Vendor_1689_Product_fd01.kl
Vendor_1689_Product_fe00.kl
Vendor_18d1_Product_2c40.kl
Vendor_18d1_Product_5018.kl
Vendor_1949_Product_0401.kl
Vendor_1bad_Product_f016.kl
Vendor_1bad_Product_f023.kl
Vendor_1bad_Product_f027.kl
Vendor_1bad_Product_f036.kl
Vendor_1d79_Product_0009.kl
Vendor_22b8_Product_093d.kl
Vendor_2378_Product_1008.kl
Vendor_2378_Product_100a.kl
comip-gpio-keys.kl
comip-keypad.kl
ft5x06.kl
h2w_headset.kl
qwerty.kl
sensor00fn11.kl


4. 使用kl文件,将扫描码,转换成按键码:

之前我们在按键流程(一),已经讲解了按键最后到各个InputMapper中的process函数中处理,下面我们看这个函数,我们调用EventHub的mapKey来将扫描码转换成按键码。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {switch (rawEvent->type) {case EV_KEY: {int32_t scanCode = rawEvent->code;int32_t usageCode = mCurrentHidUsage;mCurrentHidUsage = 0;if (isKeyboardOrGamepadKey(scanCode)) {int32_t keyCode;uint32_t flags;if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {//扫描码对应成按键码keyCode = AKEYCODE_UNKNOWN;flags = 0;}processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);}break;}case EV_MSC: {if (rawEvent->code == MSC_SCAN) {mCurrentHidUsage = rawEvent->value;}break;}case EV_SYN: {if (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}}
}

最后在processKey函数中,将发送到InputDispatch中做后续处理。这个我们在之前的博客中也分析过了。

我们再来看看mapKey函数,先处理的kcm,再处理kl的。

 

status_t EventHub::mapKey(int32_t deviceId,int32_t scanCode, int32_t usageCode, int32_t metaState,int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {AutoMutex _l(mLock);Device* device = getDeviceLocked(deviceId);status_t status = NAME_NOT_FOUND;if (device) {// Check the key character map first.sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();if (kcm != NULL) {if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {*outFlags = 0;status = NO_ERROR;}}// Check the key layout next.if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {status = NO_ERROR;}}if (status == NO_ERROR) {if (kcm != NULL) {kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);} else {*outMetaState = metaState;}}}if (status != NO_ERROR) {*outKeycode = 0;*outFlags = 0;*outMetaState = metaState;}return status;
}

至于详细分析kcm的mapKey和kl的mapKey放在以后分析了

 

 













 




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

相关文章

vm kl

典型 deb 9.x 使用mirror 无 继续 是grub sda

Vijos P1596 加法表【迭代】

加法表 背景 神奇的加法表~ 描述 著名科学家卢斯为了检查学生对进位制的理解&#xff0c;他给出了如下的一张加法表&#xff0c;表中的字母代表数字。 例如: L K V E L L K V E K K V E KL V V E KL KK E E KL KK KV 其含义为&#xff1a;LLL&#xff0c;LKK&#xff0c;LVV…

从KL散度到MLE

MIT的课程 18.650 statistics for applications 在Lecture 4 讲MLE的时候&#xff0c;和一般书本上来就给出MLE公式的方法不同&#xff0c;这里使用Max variant distance -> KLdivergence ->MLE的方式&#xff0c;初看到这个过程&#xff0c;内心感觉还是比较有意思的&a…

KL和汽车各个档位

转载自&#xff1a;CSDN博主&#xff1a;这座城市没有海&#xff0c;网址&#xff1a;https://blog.csdn.net/qq_42718749/article/details/113625135&#xff0c;如有侵权&#xff0c;请联系博主KL是德语Klemme的缩写&#xff0c;意思是ECU的管脚、接线柱&#xff0c;和Pin意思…

matlab对图像进行KL变换,kl变换特征提取

KL变换_工学_高等教育_教育专区。模式识别中经典算法KL变换的详细介绍 基于K 基于K-L变换的多类模式特征提取特征提取的目的: 对一类模式:维数压缩。 对多类模式...... (Karhunen-Loeve)变换,由原始人脸图像中提取特征 向量;分类器设计在训练过程中完成,利用 已知人脸图像样本进…

通信协议的封装

一般游戏开发都会设计到网络通信。包括客户端端和服务端&#xff0c;还哟偶服务器之间。 网络通讯多以数据包的形式发送。 最简单的格式莫过于KLV&#xff08;Key&#xff0c;Length&#xff0c;Value&#xff09;。在此之上还会有源地址 与目标地址的封装&#xff08;添加源…

Vue 常用指令

指令介绍 指令 : 带有 v- 前缀的特殊属性。 指令的作用 : 当表达式的值改变时&#xff0c;将其产生的连带影响&#xff0c;响应式地作用于 DOM 。 在整个vue的编写过程当中&#xff0c;只要带v-的&#xff0c;那么都是常用的vue的指令。 v-text v-text作用与双大花括号作用…

IF: 25+ 单细胞转录组学揭示肝实质和非实质细胞系的早期出现

&#xff0c; 桓峰基因公众号推出单细胞生信分析教程并配有视频在线教程&#xff0c;目前整理出来的相关教程目录如下&#xff1a; Topic 6. 克隆进化之 Canopy Topic 7. 克隆进化之 Cardelino Topic 8. 克隆进化之 RobustClone SCS【1】今天开启单细胞之旅&#xff0c;述说单细…