zoukankan      html  css  js  c++  java
  • Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析

    Android系统--输入系统(十)Reader线程_核心类及配置文件深入分析

    0. 前言

    个人认为该知识点阅读Android源代码会不仅容易走进死胡同,并且效果并不好,前脚看完后脚忘记,故进行总结,希望可以更好帮助大家了解,了解之后在进行阅读源代码会有事半功倍的效果。

    1. 引入

    由输入系统(九)实验得出
    • keylayout文件:只是用来表示驱动上报的scancode对应哪一个android按键(AKEYCODE_x)只是表示按键被按下。
    • kcm文件: 用来表示android按键(AKEYCODE_x)对应哪一个字符。也表示同时按下其他按键后,对应哪个字符。
    但是要想深入理解其中的读取转化过程,需要仔细分析源代码,了解其中涉及的类和结构体,本次博文就是针对其进行解析。

    2. 函数具体实现

    Event_Hub.cpp
    • open--打开设备节点
    • ioctl得到设备信息
    • 创建device对象
    • 加载IDC配置文件
    • 加载.kl,kcm文件
    status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];
    
    ALOGV("Opening device: %s", devicePath);
    
    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    if(fd < 0) {
    ALOGE("could not open %s, %s
    ", 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
    ", devicePath, strerror(errno));
    } else {
    buffer[sizeof(buffer) - 1] = '';
    identifier.name.setTo(buffer);
    }
    
    // Get device driver version.
    int driverVersion;
    if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
    ALOGE("could not get driver version for %s, %s
    ", devicePath, strerror(errno));
    close(fd);
    return -1;
    }
    
    // Get device identifier.
    struct input_id inputId;
    if(ioctl(fd, EVIOCGID, &inputId)) {
    ALOGE("could not get device input id for %s, %s
    ", devicePath, strerror(errno));
    close(fd);
    return -1;
    }
    identifier.bus = inputId.bustype;
    identifier.product = inputId.product;
    identifier.vendor = inputId.vendor;
    identifier.version = inputId.version;
    
    // Get device physical location.
    if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
    //fprintf(stderr, "could not get location for %s, %s
    ", devicePath, strerror(errno));
    } else {
    buffer[sizeof(buffer) - 1] = '';
    identifier.location.setTo(buffer);
    }
    
    // Get device unique id.
    if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
    //fprintf(stderr, "could not get idstring for %s, %s
    ", devicePath, strerror(errno));
    } else {
    buffer[sizeof(buffer) - 1] = '';
    identifier.uniqueId.setTo(buffer);
    }
    
    // Allocate device. (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    
    // Load the configuration file for the device.
    loadConfigurationLocked(device);
    
    ......
    
    addDeviceLocked(device);
    return 0;
    }
    

    3. 类调用过程

    4. EventHub类

    在Reader线程中,采用EventHub类记录多个输入设备
    - mNextDeviceId   : int32_t
    - mDevices        : KeyedVector<int32_t, Device*> //表示设备,记录设备编号和设备
    - mOpeningDevices : Device *
    - mClosingDevices : Device *
    

    5. Device类

    描述输入设备
    - fd                    : int //设备节点所打开的文件句柄
    - identifier            : const InputDeviceIdentifier //记录厂商信息,存储了设备的供应商、型号等信息
    - keyBitmask[]          : uint8_t
    - configurationFile     : String8 //IDC文件名
    - configuration         : PropertyMap* //IDC属性:(内嵌OR外接)设备
    - keyMap                : KeyMap //保存配置文件(kl,kcm)
    
    IDC文件调用顺序说明
    Input device configuration files are located by USB vendor, product (and optionally version) id or by input device name.
    
    The following paths are consulted in order.
    
    /system/usr/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
    /system/usr/idc/Vendor_XXXX_Product_XXXX.idc
    /system/usr/idc/DEVICE_NAME.idc
    /data/system/devices/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
    /data/system/devices/idc/Vendor_XXXX_Product_XXXX.idc
    /data/system/devices/idc/DEVICE_NAME.idc
    

    6. Keymap对象详解

    6.1 Keymap类
    - keyLayoutFile        : String8
    - keyLayoutMap         : sp<KeyLayoutMap>
    - keyCharacterMapFile  : String8
    - keyCharacterMap      : sp<KeyCharacterMap>
    
    6.2 KeyLayoutMap类
    - mKeysByScanCode     : KeyedVector<int32_t, Key> //内核上报的扫描码,返回key结构体
    - mKeysByUsageCode    : KeyedVector<int32_t, Key> //使用usrage码上报,少用(USB键盘等)
    
    • key结构体
    - keyCode : int32_t //对应Android系统返回的AKEYCODE码
    - flags   : uint32_t
    /*flags参数说明*/
    /*The following policy flags are recognized:
    *FUNCTION:The key should be interpreted as if the FUNCTION key were also pressed.
    *GESTURE: The key generated by a user gesture, such as palming the touchscreen.
    *VIRTUAL: The key is a virtual soft key (capacitive button) adjacent to the main touch screen. This causes special debouncing logic to be enabled (see below).
    */
    
    总结:通过内核上报的扫描码,查找并取出对应Android系统中的AKEYCODE_XXX码。
    6.3 KeyCharacterMap类
    - mKeys : KeyedVector<int32_t, Key*>
    
    //在kcm文件中也可以进行按键的映射,原理跟kl文件一样
    //但在虚拟按键中处理效果差,故一般通过kl文件进行映射
    - mKeysByScanCode : KeyedVector<int32_t, int32_t>
    - mKeysByUsageCode : KeyedVector<int32_t, int32_t>
    
    • key结构体
    - label  : char16_t
    - number : char16_t //在安卓系统中,操作只能输入数字框,按下按键则输出该参数
    - firstBehavior : Behavior*
    
    • Behavior结构体
    - metaState : int32_t
    - character : char16_t
    - fallbackKeyCode : int32_t
    
    参数具体说明:用kcm文件中B字符举例,下面根据Android中的AKEYCODE_B(例1),得出对应的key结构体,结构体包括label、number、firstBehavior三个参数,label参数:对应例1中的label;number参数:在安卓系统中,操作只能输入数字框,按下按键则输出该参数;对于例1中base会生成一个behavior结构体描述,即behavior结构体中的metaState参数为0,character为‘b’,同样shift,也会构建一个behavior结构体,此时mstaState为shift,character为'A',capslock与shift同理。
    例1:KCM文件(AKEYCODE_B)
    key B {
    label:               'B' # 印在按键上的文字
    base:                'b' # 如果没有其他按键(shift, ctrl等)同时按下,此按键对应的字符是'b'
    shift, capslock:     'B' # 当按下shift/capslock,输出为'B'
    }
    
    对于fallbackKeyCode 参数说明:同理AKETOCDE_SPACE(例2),前面对应关系就不赘述,从例2中的base开始分析,对于base会生成一个behavior结构体描述,即behavior结构体中的metaState参数为0,character为‘ ’,alt,也会构建一个behavior结构体,此时mstaState为shift,character无,fallbackKeyCode为AKEYCODE_SEARCH,meta与alt与同理。crtl也会构建一个behavior结构体,此时mstaState为crtl,character无,fallbackKeyCode为AKEYCODE_LANGUAGE_SWITCH。
    对应的behavior结构体构建为一条链表,firstBehavio即指向该链表。
    fallbackKeyCode作用:底层的Linux驱动检测有输入事件产生,上报一个Android:keycode,应用程序收到后进行处理,如果可以处理,处理之后会回复一个处理成功的信号,如果不可以处理,输入系统会再次上报一个值,即为fallbackKeyCode。
    例2:KCM文件(AKEYCODE_SAPCE)
    key SPACE {
    label:               ' '
    base:                ' '
    alt,meta:            'fallback SEARCH'
    ctrl:                'fallback LANGUAGE_SWITCH'
    }
    
    7. 类的调用过程概述
    • Reader线程使用EventHub类记录多个输入设备
    • 从EventHub类中通过Device类,根据设备编号查找并打开对应输入设备
    • 打开设备,并通过ioctl获取该设备信息
    • 根据设备信息读取IDC文件,加载keylayout、KCM配置文件(保存在KeyMap类中)
    • keylayout负责将Linux内核上报的扫码转化为Android中的AKEYCODE_XXX
      • 读取一个kl文件
      • 扫描码转为keycode码
      • 将信息存在KeyedVector当中key结构中
    • KCM文件负责将keycode码转化为输出字符
      • 根据keycode在KeyedVector扫描出对应的key结构体
      • key结构体中的firstBehavior结构体指针指向一系列的behavior结构体
      • 根据mateState找出对应的behavior结构体,返回对应的character
      • 如果无法处理,则输入系统将再次上报一个值,即为fallbackKeyCode
  • 相关阅读:
    ASP.NET MVC案例——————拦截器
    Windows Azure Virtual Network (10) 使用Azure Access Control List(ACL)设置客户端访问权限
    Windows Azure Storage (20) 使用Azure File实现共享文件夹
    Windows Azure HandBook (5) Azure混合云解决方案
    Windows Azure Service Bus (6) 中继(Relay On) 使用VS2013开发Service Bus Relay On
    Azure PowerShell (9) 使用PowerShell导出订阅下所有的Azure VM的Public IP和Private IP
    Windows Azure Service Bus (5) 主题(Topic) 使用VS2013开发Service Bus Topic
    Azure China (9) 在Azure China配置CDN服务
    Windows Azure Storage (19) 再谈Azure Block Blob和Page Blob
    Windows Azure HandBook (4) 分析Windows Azure如何处理Session
  • 原文地址:https://www.cnblogs.com/lkq1220/p/6783256.html
Copyright © 2011-2022 走看看