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

在之前按键过程分析的几篇博客中,我分析过关于按键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,各种打印。
// 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  
  1. 加载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文件
[cpp] view plain copy
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文件。
[plain] view plain copy
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 = 1  

keyboard.layout = qwerty  
keyboard.characterMap = qwerty  
keyboard.orientationAware = 1  
keyboard.builtIn = 1  

cursor.mode = navigation  
cursor.orientationAware = 1  
  1. 加载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文件了
[cpp] view plain copy
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* 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 map = new KeyLayoutMap();  
        if (!map.get()) {  
            ALOGE("Error allocating key layout map.");  
            status = NO_MEMORY;  
        } else {  
#if DEBUG_PARSER_PERFORMANCE  
            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);  
#endif  
            Parser parser(map.get(), tokenizer);  
            status = parser.parse();  
#if DEBUG_PARSER_PERFORMANCE  
            nsecs_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);  
#endif  
            if (!status) {  
                *outMap = map;  
            }  
        }  
        delete tokenizer;  
    }  
    return status;  
}  
下面的数字代表扫描码,而旁边的代码键值码。
[plain] view plain copy
key 113   VOLUME_MUTE  
key 114   VOLUME_DOWN  
key 115   VOLUME_UP  
key 116   POWER  
这样就完成了kl文件的加载解析。我们看下这段的log
[plain] view plain copy
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  
  1. 使用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的。
[cpp] view plain copy
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 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放在以后分析了

你可能感兴趣的:(Android5.0 按键kl文件加载过程分析)