Input event 分析

以gsensor器件为例:

1、Gsensor上报ABS_ DISTANCE后,接着上报sync事件,当event core 检测到sync事件时,认为一包event数据完成【参考下文代码得出】。

    static void XXX_pls_report_dps(unsigned char data, struct input_dev *input)

{   
         ……
         input_report_abs(input, ABS_DISTANCE, dps_data);
         input_sync(input);
}

**2、 input_report_abs 被转化为 input_event + EV_ABS 事件形式上报:
input_sync被转化为 input_event + EV_SYN 事件形式上报**

input_report_abs(input, ABS_DISTANCE, dps_data) ---->input_event(dev, EV_ABS, code, value)
input_sync(input) ----->input_event(dev, EV_SYN, SYN_REPORT, 0)

3、input_event函数解析

     void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
    {
        unsigned long flags;
        if (is_event_supported(type, dev->evbit, EV_MAX)) {

//首先会判断该输入设备是否支持给type类型的事件;如果设备要支持某种类型的事件,需要在设备注册前使用__set_bit(EV_type, input_dev->evbit)函数设置input_dev支持EV_type类型事件。

       `spin_lock_irqsave(&dev->event_lock, flags);` 

// 中断保护自旋锁,保护一次input event事件上报完成后,下一次获取到中断保护自旋锁事件才可被处理。如果没有获取到自旋锁,则线程一直在此等待完成。

           input_handle_event(dev, type, code, value);
           spin_unlock_irqrestore(&dev->event_lock, flags);
        }
}

4、 input_handle_event(dev, type, code, value);

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)
{
    int disposition;

    disposition = input_get_disposition(dev, type, code, value);

//判断事件的处理方式,注意观察disposition 变量的使用;此函数稍后分析。

if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
        dev->event(dev, type, code, value);

    if (!dev->vals)
        return;

    if (disposition & INPUT_PASS_TO_HANDLERS) {

//如果事件允许进入后续的handler 处理,则先将事件加入到设备事件数组中(dev->vals)

    struct input_value *v;

        if (disposition & INPUT_SLOT) { 
   // `INPUT_SLOT` 多点输入设备,如`Touchpanel`。            
            v = &dev->vals[dev->num_vals++];
            v->type = EV_ABS;
            v->code = ABS_MT_SLOT;
            v->value = dev->mt->slot;
        }

        v = &dev->vals[dev->num_vals++];
        v->type = type;
        v->code = code;
        v->value = value;
    }
    if (disposition & INPUT_FLUSH) {

// INPUT_FLUSH 标志设备输入事件发送dev->vals 数组将被下次事件覆盖

f (dev->num_vals >= 2)
            input_pass_values(dev, dev->vals, dev->num_vals);  

// 重点handler后续处理

dev->num_vals = 0;
    } else if (dev->num_vals >= dev->max_vals - 2) {
        dev->vals[dev->num_vals++] = input_value_sync;
        input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    }

}

5、 input_get_disposition(dev, type, code, value)重要函数,判定事件的处理方式。

static int input_get_disposition(struct input_dev *dev,
              unsigned int type, unsigned int code, int value)
{
    int disposition = INPUT_IGNORE_EVENT;

    switch (type) {

       ……..
    case EV_ABS:
        if (is_event_supported(code, dev->absbit, ABS_MAX)) 

//判断设备是否支持ABS输入事件;如设备要支持,需要在注册输入设备前调用 input_set_capability(input_dev, EV_ABS, ABS_DISTANCE)【表示设备支持ABS_DISTANCE(code)EV_ABS (type)事件 】

    `disposition = input_handle_abs_event(dev, code, &value);`

//此函数会根据输入事件的code值及 absinfo[code].value(设备abs原始值)判定对于此次事件的处理方式,注意如果:初始化的absinfo[code].value 值与设备初次上包的值一致,则设备第一次的上报事件将被忽略。

        break;
       ……
    return disposition;
}

6、input_pass_values(dev, dev->vals, dev->num_vals) —> input_to_handler(handle, vals, count) —-> handler->events(handle, vals, count)—> evdev_events(handle,input_value, count) —>evdev_pass_values(evdev_client, input_value, count,ktime_t , ktime_t)

input_to_handler( )函数首先调用handler->filter函数对上报的事件进行过滤,可以客制化此函数来过滤需要忽略的事件。之后调用handler->events 函数对过滤后的事件进行处理。handler->events 会调用evdev_events 函数。

evdev_events()函数在evdev.c文件中,它调用evdev_pass_values函数。evdev_pass_values函数进行两步重要处理:首先将事件按发送顺序放入evdev_client->buffer中(环形buf);之后如果事件type=EV_SYN,code=SYN_REPORT,则有可能执行两种动作: 1、调用kill_fasync向指定的fasync_struct进程发送SIGIO信号【该进程属于用户进程,及应用层进程】;2、使用wake_up_interruptible函数唤醒以阻塞方式获取输入事件进程。在执行这两种动作的同时会将evdev_client->buffer中的client->packet_head包头重新指向buffer的头部。自此input eventkernel中上报的流程进行完毕。接下来分析用户层是怎样从kenerl层获取input event事件的。

7、 上层获取input event 事件的方式可以查看getevent 命令(system/ core/toolbox)

Getevent命令实现中以非阻塞的方式打开input设备,然后以poll的方式获取input设备状态;每次poll结束时会对poll的返回状态进行分析,返回有POLLIN状态时会调用read函数获取设备的input event事件。

应用层的poll函数会间接的调用kernel层的evdev_poll函数,evdev_poll函数中当 client->packet_head 包头不等 client->tail 时返回POLLIN状态;从getevent进入读取设备状态环节,应用层的read 函数回调调用到设备层的evdev_read函数,evdev_read函数会将kernel层的inpet event数据copy到用户空间;如果getevent是以阻塞方式打开input设备的话,evdev_read函数中读取完输入事件后需要等待evdev->wait事件被唤醒后才可能返回。

8、 补充自动repeat事件配

Repeat事件是指相同key事件的多次上报,在input device注册过程中会进行repeat事件相关的timer、回调函数、延时启动时间、启动周期等设置。当key事件按下的时程序中自动启动repeat 事件监测timer,无论哪个key事件被释放则此timer被终止。当timer超时后会调input_repeat_key此函数首先会再次发送 repeat_key 和 syn 事件,注意repeat_key 的value值为2、sync事件的value为1,与通常的键值不同此值表示为repeat;随后重新启动repeat timer。

### 解析和分析 Android Input 日志的方法 在 Android 中,Input Manager Service (IMS) 是负责处理输入事件的核心组件之一。要解析和分析与 `Input` 相关的日志,可以通过以下方式实现: #### 1. 使用 Logcat 工具捕获日志 Logcat 是 Android 提供的一个强大的工具,用于捕获设备上的运行时日志信息。对于 `InputManagerService` 的调试,可以过滤特定标签或优先级来获取相关日志。 命令如下: ```bash adb logcat | grep "Input" ``` 此命令会筛选出所有包含关键字 `"Input"` 的日志条目[^1]。如果需要更精确的过滤,还可以指定日志级别(如 DEBUG 或 VERBOSE),例如: ```bash adb logcat -v threadtime Input:V *:S ``` 上述命令仅显示来自 `Input` 类别的详细日志,并屏蔽其他无关类别。 --- #### 2. 配置 Debug 参数以启用额外日志 某些情况下,默认日志可能不足以满足需求。可以在启动系统服务之前设置环境变量或者修改源码配置文件,强制开启更多详细的调试信息。 例如,在 AOSP 源码中找到 `InputManagerService.java` 文件并调整其内部的 LOG_TAG 定义为 true 来增加输出量: ```java private static final boolean DEBUG = true; ``` 重新编译项目后部署到目标设备上即可获得增强版日志记录功能。 --- #### 3. 利用 Memory Logs 进行深入剖析 除了常规的日志打印外,当遇到复杂场景比如卡顿现象或是异常崩溃时,则需借助内存快照技术进一步诊断原因所在。按照先前提到过的关于 **Android 内存日志** 实践指南[^2] ,能够有效定位潜在瓶颈位置以及评估整体表现状况。 具体操作步骤包括但不限于导出 HPROF 数据包、加载至 MAT(Heap Analyzer Tools)界面查看对象分配链路图谱等等手段相结合完成全面审查工作流。 --- #### 示例代码片段展示如何自定义拦截器抓取按键动作轨迹 下面给出一段简单的 Java 示例程序演示怎样创建广播接收者监听全局范围内的键盘敲击行为并向控制台发送通知消息: ```java public class KeyEventListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ("android.intent.action.KEY_EVENT".equals(action)) { KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); int keyCode = event.getKeyCode(); long timestamp = System.currentTimeMillis(); Log.d("KeyEventListener", "KeyCode:" + keyCode + ", Timestamp:" + timestamp); } } } ``` 注册该类实例以便实时响应各类物理按钮触发信号变化过程从而便于后续统计汇总用途。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值