Android 输入系统解析 (1)

有两种思路来解析Android Input 子系统的代码(注一),一是从应用的事件获取开始,自上而下的解析Input相关代码的功能及调用关系(从 注二  的内容可知,输入事件是通过View类中的事件监听接口获取的);二是从内核驱动开始,自下而上找出调用关系;本文采用第二种思路。

1. 内核输入驱动

内核输入驱动包括两个类别协同工作的驱动,即输入设备驱动和输入事件驱动,设备驱动负责与输入的硬件设备进行通讯,而事件驱动提供供用户空间应用访问的接口(即 /dev/input/xxx 设备节点)。因为输入驱动已提供了标准化的事件接口,新写一个事件驱动相对来说较为简单,大致步骤为:
a). 初始化时调用标准函数input_allocate_device分配一个input_dev结构体并设置要产生的事件及事件码
b).  调用input_register_device来注册一个输入事件驱动
c).  调用input_report_xxx(rel/abs…)和input_sync来上报事件。

2. EventHub

应用程序获得输入事件的方法为打开输入设备节点并调用poll来查询和read来读取输入事件。在Android中,读取输入事件在 frameworks/base/services/input/EventHub.cpp中实现。EventHub类中有两个关键成员函数: openDeviceLocked和getEvents,它们的作用分别为打开设备节点和读取输入事件。

openDeviceLocked实现的具体功能为:

  •  打开设备节点
  • 获得设备名
  • 检查设备是否在不支持设备之列
  • 获得设备版本号和标识符
  • 获得设备唯一的ID
  • 为使用poll函数,将文件描述符设为非阻塞类型
  • 获得设备类别(输入设备类别在EventHub.h中定义,具体如下)
    INPUT_DEVICE_CLASS_KEYBOARD    键盘
    INPUT_DEVICE_CLASS_ALPHAKEY    数字键盘
    INPUT_DEVICE_CLASS_TOUCH           触摸屏(单点或多点)
    INPUT_DEVICE_CLASS_CURSOR         光标(如轨迹球或鼠标)
    INPUT_DEVICE_CLASS_TOUCH_MT  多点触摸屏
    INPUT_DEVICE_CLASS_DPAD              方向键
    INPUT_DEVICE_CLASS_GAMEPAD     游戏手柄
    INPUT_DEVICE_CLASS_SWITCH         开关键
    INPUT_DEVICE_CLASS_JOYSTICK     操纵杆
    INPUT_DEVICE_CLASS_EXTERNAL   外部设备

getEvents调用read函数来读取输入设备上报的事件并直接将事件信息填充进RawEvent结构体数组缓冲区(即传入参数buffer)中。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
size_t EventHub::getEvents( int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
 
     ...
     for (;;) {
         ....
         // Grab the next input event.
         bool deviceChanged = false ;
         while (mPendingEventIndex < mPendingEventCount) {
             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
             if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                 if (eventItem.events & EPOLLIN) {
                     mPendingINotify = true ;
                 } else {
                     LOGW( "Received unexpected epoll event 0x%08x for INotify." , eventItem.events);
                 }
                 continue ;
             }
 
             if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                 if (eventItem.events & EPOLLIN) {
                     LOGV( "awoken after wake()" );
                     awoken = true ;
                     char buffer[16];
                     ssize_t nRead;
                     do {
                         nRead = read(mWakeReadPipeFd, buffer, sizeof (buffer));
                     } while ((nRead == -1 && errno == EINTR) || nRead == sizeof (buffer));
                 } else {
                     LOGW( "Received unexpected epoll event 0x%08x for wake read pipe." ,
                             eventItem.events);
                 }
                 continue ;
             }
 
             ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
             if (deviceIndex < 0) {
                 LOGW( "Received unexpected epoll event 0x%08x for unknown device id %d." ,
                         eventItem.events, eventItem.data.u32);
                 continue ;
             }
 
             Device* device = mDevices.valueAt(deviceIndex);
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize = read(device->fd, readBuffer,
                         sizeof ( struct input_event) * capacity);
                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                     // Device was removed before INotify noticed.
                     LOGW( "could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n" ,
                          device->fd, readSize, bufferSize, capacity, errno );
                     deviceChanged = true ;
                     closeDeviceLocked(device);
                 } else if (readSize < 0) {
                     if ( errno != EAGAIN && errno != EINTR) {
                         LOGW( "could not get event (errno=%d)" , errno );
                     }
                 } else if ((readSize % sizeof ( struct input_event)) != 0) {
                     LOGE( "could not get event (wrong size: %d)" , readSize);
                 } else {
                     int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
                     size_t count = size_t (readSize) / sizeof ( struct input_event);
                     for ( size_t i = 0; i < count; i++) {
                         const struct input_event& iev = readBuffer[i];
                         LOGV( "%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d" ,
                                 device->path.string(),
                                 ( int ) iev. time .tv_sec, ( int ) iev. time .tv_usec,
                                 iev.type, iev.code, iev.value);
 
#ifdef HAVE_POSIX_CLOCKS
                         // Use the time specified in the event instead of the current time
                         // so that downstream code can get more accurate estimates of
                         // event dispatch latency from the time the event is enqueued onto
                         // the evdev client buffer.
                         //
                         // The event's timestamp fortuitously uses the same monotonic clock
                         // time base as the rest of Android.  The kernel event device driver
                         // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
                         // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
                         // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
                         // system call that also queries ktime_get_ts().
                         event->when = nsecs_t(iev. time .tv_sec) * 1000000000LL
                                 + nsecs_t(iev. time .tv_usec) * 1000LL;
                         LOGV( "event time %lld, now %lld" , event->when, now);
#else
                         event->when = now;
#endif
                         event->deviceId = deviceId;
                         event->type = iev.type;
                         event->scanCode = iev.code;
                         event->value = iev.value;
                         event->keyCode = AKEYCODE_UNKNOWN;
                         event->flags = 0;
                         if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
                             status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
                                         &event->keyCode, &event->flags);
                             LOGV( "iev.code=%d keyCode=%d flags=0x%08x err=%d\n" ,
                                     iev.code, event->keyCode, event->flags, err);
                         }
                         event += 1;
                     }
                     capacity -= count;
                     if (capacity == 0) {
                         // The result buffer is full.  Reset the pending event index
                         // so we will try to read the device again on the next iteration.
                         mPendingEventIndex -= 1;
                         break ;
                     }
                 }
             } else {
                 LOGW( "Received unexpected epoll event 0x%08x for device %s." ,
                         eventItem.events, device->identifier.name.string());
             }
         }
     }
     ....
}

注一: 本文所使用Android代码的版本为: 4.0.3
注二: http://developer.android.com/guide/topics/ui/ui-events.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值