前言:
在网上看到好多关于android input device流程分析,但是都不全,有的只是从linux内核那边分析,有的从android上层分析,而且分析的代码也比较老,都是在android2.3以下,最近在做android4.0下的多点触摸以及校准程序,多点触摸的驱动很好写,在linux内核里面都有现成的例子,照着改就可以了。但是android下的校准程序比较复杂,一种是在android
Framework层进行,一种是在linux 内核层进行。
对于校准程序来说,需要全屏校准。但是在android4.0下面,下面的导航栏是systemui画的,无法去掉,因此在校准程序里面通过display得到分辨率高度比实际的小,差的那部分就是导航栏的高度。如果以小的高度进行校准,但使用实际的高度进行触摸坐标到屏幕坐标转换,就会导致触摸点偏下的问题。
为了解决这个问题,在网上找了很多资料,第一种就是想办法在校准程序里面得到整个屏幕的分辨率,进而让校准程序全屏显示,即把导航栏隐藏,在网上看到又网友用下面例子实现:
//for phone
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
//forpad View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN= 4
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_SHOW_FULLSCREEN);
经过自己实验,这两个都无法隐藏下面的导航栏,而且在最新的sdk里面也没有SYSTEM_UI_FLAG_SHOW_FULLSCREEN的定义。第二种就是在jni种通过fb0得到系统的分辨率,这个是真实的分辨率,这种方法需要apk有root或者graphics组权限,才能打开fb0,而且android4.0根据触摸屏类型是否使用外部显示分辨率,如果使用外部display的话,那么就不能用fb0的分辨率。为了解决这个问题,把整个input touch流程都看了一边。废话少说,进入正题。
Android inout touch流程分两部分,一部分是从androidframework开始,如何读取touch设备的事件并分发。一部分是从linux 内核开始,如何从触摸屏读取触摸坐标并送给touch设备。
首先看看Event Input文件结构吧,在frameworks/base/services/input之下
l Eventhub
它是系统中所有事件的中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。EventHub通过getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如输入设备的类别,输入设备支持哪些按键。
l InputReader
InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper处理之后输入对应的input listener.InputReader拥有一个InputMapper集合。它做的大部分工作在InputReader线程中完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的Mutex来保护它的状态。InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。
l InputDispatcher
InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的policy对象控制。
l InputManager
InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出气筒。
InputManager使用两个线程:
1)InputReaderThread叫做"InputReader"线程,它负责读取并预处理RawEvent,applies policy并且把消息送入DispatcherThead管理的队列中。
2)InputDispatcherThread叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件分发给应用程序。
InputReaderThread类与InputDispatcherThread类不共享内部状态,所有的通信都是单向的,从InputReaderThread到InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。
InputManager类从不与Java交互,而InputDispatchPolicy负责执行所有与系统的外部交互,包括调用DVM业务。
看看下图理解input下面几个模块的关系
SystemServer大家熟悉吧,它是android init进程启动的,它的任务就是启动android里面很多服务,并管理起来,如果大家不熟悉,请参考andorid启动流程分析
SystemServer.java(frameworks\base\services\java\com\android\server)里面
ServerThread::run调用
Slog.i(TAG,"Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
WindowManagerService.java(frameworks\base\services\java\com\android\server\wm) 里面
WindowManagerService main调用
WMThread thr = new WMThread(context, pm, haveInputMethods,allowBootMsgs);
thr.start();
接着调用
WMThread:: run调用
WindowManagerService s = new WindowManagerService(mContext, mPM,
mHaveInputMethods, mAllowBootMessages);
接着在WindowManagerService里面调用
mInputManager = new InputManager(context,this);
至此我们创建了一个java层input设备管理器
InputManager.java(frameworks\base\services\java\com\android\server\wm) 里面
InputManager调用
nativeInit(mContext, mCallbacks,looper.getQueue());
从下面开始就进入native空间
com_android_server_InputManager.cpp(frameworks\base\services\jni)里面
nativeInit对应android_server_InputManager_nativeInit调用
gNativeInputManager = newNativeInputManager(contextObj, callbacksObj, looper);
在NativeInputManager里面调用
sp
mInputManager = new InputManager(eventHub, this, this);
这个函数创建一个EventHub对象,然后把它作为参数来创建InputManager对象。特别注意,InputManager是在C++里,具体在InputManager.cpp里。EventHub类在EventHub.cpp里,这个类和input事件获取有关。
至此我们创建了一个native层input设备管理器,具体作用见上面说明
首先是去InputManager.cpp(frameworks\base\services\input) 文件里面
InputManager::InputManager调用
mDispatcher = newInputDispatcher(dispatcherPolicy);
mReader= new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
它创建了InputDispatcher对象,同时也创建了InputReader对象。并分别暂存于mDispatcher和mReader变量中。注意eventHub和mDispatcher都作为参数创建InputReader对象。后面还用initialize来初始化。下面是initialize函数的定义:
void InputManager::initialize() {
mReaderThread = newInputReaderThread(mReader);
mDispatcherThread = newInputDispatcherThread(mDispatcher);
}
它创建两个线程对象,一个是InputReaderThread线程对象,负责input事件的获取;另一个是InputDispatcherThread线程对象,负责input消息的发送。
(注:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建)
InputDispatcher.cpp(frameworks\base\services\input) 里面
InputDispatcher::InputDispatcher做一些准备工作
InputReader.cpp (frameworks\base\services\input)里面
InputReader::InputReader做一些准备工作
在上面讲到在WindowManagerService里面调用
mInputManager = new InputManager(context, this);
创建input 管理器
紧接着调用
mInputManager.start();
InputManager.java(frameworks\base\services\java\com\android\server\wm) 里面
start调用
Slog.i(TAG, "Starting inputmanager");
nativeStart();
从下面开始就进入native空间
com_android_server_InputManager.cpp(frameworks\base\services\jni)里面
nativeStart对应android_server_InputManager_nativeStart调用
status_t result =gNativeInputManager->getInputManager()->start();
InputManager.cpp(frameworks\base\services\input) 文件里面
InputManager::start调用
status_tresult = mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);
result= mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
上面两个线程对象是Thread子类,于是继承它的run方法,在Thread::run中,调用createThreadEtc函数,并以Thread::_threadLoop作为入口函数,以上面的mDispatcherThread或mReaderThread作为userdata创建线程,然后会调用threadLoop(),在Thread类中它是虚函数,得由子类来复写
因此会调用InputReader.cpp (frameworks\base\services\input)里面的threadLoop InputReaderThread::threadLoop调用
mReader->loopOnce();
mReader就是上面创建的inputreader对象,作为参数传给mReaderThread
InputReader::loopOnce调用
count = mEventHub->getEvents(timeoutMillis, mEventBuffer,EVENT_BUFFER_SIZE);
得到input 输入事件
processEventsLocked
处理input输入事件
因此会调用InputDispatcher.cpp (frameworks\base\services\input)里面的threadLoop InputDispatcherThread::threadLoop调用
mDispatcher->dispatchOnce ();
mDispatcher就是上面创建的InputDispatcher对象,作为参数传给mDispatcherThread
InputDispatcher::dispatchOnce调用
dispatchOnceInnerLocked(&nextWakeupTime)
dispatchOnceInnerLocked函数处理input输入消息,mLooper->pollOnce是等待下一次输入事件。
mLooper->pollOnce(timeoutMillis):
这个请看Looper.cpp文件中的Looper::pollOnce()函数。Looper里主要通过linux管道方式实现进程间通信,通过epoll机制实现外界事件请求作出响应。
至此整个androidinput event框架已经运转起来了,好像到现在还没有提到touch,别着急,且看下面的分析
还记得android_server_InputManager_nativeInit里面创建
sp
EventHub.cpp (frameworks\base\services\input) 里面
EventHub::EventHub初始化
mOpeningDevices(0) 表示需要打开的设备链表,为NULL
mClosingDevices(0) 表示需要关闭的设备链表,为NULL
mNeedToSendFinishedDeviceScan(false) 表示需要发送设备扫描完成,默认为0
mNeedToReopenDevices(false) 表示需要重新打开设备,默认为0
mNeedToScanDevices(true) 表示需要扫描设备,默认为1
mPendingEventCount(0) 表示需要处理event个数,默认为0
mPendingEventIndex(0) 表示当前需要处理event的索引,默认为0
mPendingINotify(false) 表示需要处理的通知,默认为0
mEpollFd = epoll_create(EPOLL_SIZE_HINT); epoll实例,在EventHub::EventHub中初始化此例,所有输入事件通过epoll_wait来获取
mINotifyFd = inotify_init();
intresult = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
创建mINotifyFd,用于监控/dev/input目录下删除和创建设备节点的事件
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd,&eventItem)
将mINotifyFd注册到mEpollFd里面,通过epoll来监听mINotifyFd的变化
result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK)
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK)
创建唤醒管道,并设置为非阻塞,如果向mWakeWritePipeFd写,那么mWakeReadPipeFd就会有变化
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd,&eventItem);
将mWakeReadPipeFd注册到mEpollFd里面,通过epoll来监听mWakeReadPipeFd的变化
至此EventHub对象以及构造完成了,mEpollFd监听mINotifyFd和mWakeReadPipeFd的变化。
在上面2.4节最后我们看到InputReaderThread线程里面会循环调用
InputReader::loopOnce 接着调用
count = mEventHub->getEvents(timeoutMillis, mEventBuffer,EVENT_BUFFER_SIZE);
这里的mEventHub就是上节实例化的eventhub,我们来看getEvents
EventHub.cpp (frameworks\base\services\input) 里面
EventHub::getEvents
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
进入for循环
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
LOGI("Reopening all input devices due to a configurationchange.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
检查mNeedToReopenDevices是否为ture,如果为true,在closeAllDevicesLocked里面关闭所有打开的硬件设备描述符,并把需要删除的设备放在mClosingDevices链表里面,如果这个设备是在mOpeningDevices里面,就忽略跳过,并删除eventhub层的device对象。然后设置mNeedToScanDevices为true,因为mNeedToReopenDevices默认为false,所以不会执行这段代码
// Report any devices that had last been added/removed.
while (mClosingDevices) {
Device* device = mClosingDevices;
LOGV("Reporting device closed: id=%d, name=%s\n",
device->id,device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 :device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
检查mClosingDevices链表是否存在,如果存在,循环把需要删除的设备信息放在event里面,同时设置event type为DEVICE_REMOVED,并删除eventhub层的device对象。设置mNeedToSendFinishedDeviceScan为true。每循环一次,capacity减1,capacity等于0,就退出for循环,表明这次getEvents已经取得256个event事件了,返回给inputreader处理。因为一开始mClosingDevices不存在,所以不会执行这段代码,只有上面的closeAllDevicesLocked执行了,才会执行这段代码。
if(mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
检查mNeedToScanDevices是否为true,如果为true,就执行设备扫描。在scanDevicesLocked里面,会打开/dev/input目录,并把循环调用openDeviceLocked,在openDeviceLocked里面
int fd = open(devicePath, O_RDWR)
打开一个input设备
// Check to see if the device is on ourexcluded list
for (size_t i = 0; i
const String8& item =mExcludedDevices.itemAt(i);
if (identifier.name == item) {
LOGI("ignoring event id %sdriver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}
判断这个设备是否已经存在,如果存在,就关闭退出。
下面得到设备一系列信息。
Device* device= new Device(fd, deviceId, String8(devicePath), identifier);
创建一个eventhub层的device对象
// Load theconfiguration file for the device.
loadConfigurationLocked(device);
得到设备的idc配置文件,这就是为什么android4.0需要idc文件
// Figure outthe kinds of events the device reports.
ioctl(fd, EVIOCGBIT(EV_KEY,sizeof(device->keyBitmask)), device->keyBitmask);
ioctl(fd, EVIOCGBIT(EV_ABS,sizeof(device->absBitmask)), device->absBitmask);
ioctl(fd, EVIOCGBIT(EV_REL,sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW,sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED,sizeof(device->ledBitmask)), device->ledBitmask);
ioctl(fd,EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
得到设备各种配置,接下设置device的class,就设备的类型
// See if thisis a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X,device->absBitmask)
&&test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// Some joysticks such as the PS3controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
if (test_bit(BTN_TOUCH,device->keyBitmask) || !haveGamepadButtons) {
device->classes |=INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touchdriver?
} else if (test_bit(BTN_TOUCH,device->keyBitmask)
&& test_bit(ABS_X,device->absBitmask)
&& test_bit(ABS_Y,device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
}
上面就是根据驱动程序里面的设置来判断inputdevice是多点触摸还是单点触摸,现在是不是看到和触摸屏有点关系了
// Determinewhether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |=INPUT_DEVICE_CLASS_EXTERNAL;
}
判断是不是外部设备,根据两个条件判断,一是在idc文件里面如果有“device.internal”存在,就是内部设备,否则是外部设备。如果没有这个域存在,根据硬件设备的总线判断,如果是usb和bluetooth bus,就是外部设备。这个留着后面有作用。
if(epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem))
将设备加入到mEpollFd监控里面
device->next= mOpeningDevices;
mOpeningDevices = device;
将设备加入需要打开设备链表里面
至此,/dev/input/下面所有的设备对于linux层都已经打开,并且都添加到了mEpollFd监控里面,但是android层面的device还没有添加和初始化,只是放在需要打开设备链表里面。接着设置mNeedToSendFinishedDeviceScan为true。因为mNeedToScanDevices初始化为true,因此第一次进入getEvents就会执行这部分代码。
while(mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
LOGV("Reporting device opened: id=%d, name=%s\n",
device->id,device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 :device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
检查mOpeningDevices链表是否存在,如果存在,循环把需要添加的设备信息放在event里面,同时设置event type为DEVICE_ADDED。设置mNeedToSendFinishedDeviceScan为true。每循环一次,capacity减1,capacity等于0,就退出for循环,表明这次getEvents已经取得256个event事件了,返回给inputreader处理。因为一开始会执行mNeedToScanDevices代码,只要/dev/input下面有设备节点存在,mOpeningDevices也会存在,所以开始就会执行这段代码。
if(mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
如果mNeedToSendFinishedDeviceScan为true,就把FINISHED_DEVICE_SCAN信息放在event里面,同时capacity减1,capacity等于0,就退出for循环,表明这次getEvents已经取得256个event事件了,返回给inputreader处理。
至此mEpollFd监听mINotifyFd,mWakeReadPipeFd和/dev/input里面所有设备的变化
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex const struct epoll_event&eventItem = mPendingEventItems[mPendingEventIndex++]; 上面这段代码通过mPendingEventIndex和 mPendingEventCount关系判断mEpollFd是否监听到了事件发生,如果有事件发声,从mPendingEventItems取出一个事件 if (eventItem.data.u32 ==EPOLL_ID_INOTIFY) { if (eventItem.events &EPOLLIN) { mPendingINotify = true; } else { LOGW("Receivedunexpected epoll event 0x%08x for INotify.", eventItem.events); } continue; } 判断是否是EPOLL_ID_INOTIFY事件,即mINotifyFd有没有变化,也就是在有没有设备热拔插发声,如果有设置mPendingINotify为true,继续循环取下一个事件。如果不是继续往下走。 if (eventItem.data.u32 ==EPOLL_ID_WAKE) { if (eventItem.events &EPOLLIN) { LOGV("awoken afterwake()"); 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("Receivedunexpected epoll event 0x%08x for wake read pipe.", eventItem.events); } continue; } 判断是不是EPOLL_ID_WAKE事件,即mWakeReadPipeFd有没有变化,如果有设置awoken为true,继续循环取下一个事件。如果不是EPOLL_ID_WAKE事件,继续往下走,肯定是有设备输入事件发生。 ssize_t deviceIndex =mDevices.indexOfKey(eventItem.data.u32); if (deviceIndex < 0) { LOGW("Received unexpectedepoll event 0x%08x for unknown device id %d.", eventItem.events,eventItem.data.u32); continue; } 得到有事件发生的设备索引,如果deviceIndex < 0,有错误发声,继续循环取下一个事件。 Device* device =mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN){ int32_t readSize =read(device->fd, readBuffer, sizeof(structinput_event) * capacity); if (readSize == 0 || (readSize< 0 && errno == ENODEV)) { // Device was removedbefore INotify noticed. LOGW("could not getevent, 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 getevent (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 const structinput_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); #ifdefHAVE_POSIX_CLOCKS // Use the timespecified in the event instead of the current time // so that downstreamcode can get more accurate estimates of // event dispatchlatency from the time the event is enqueued onto // the evdev clientbuffer. // // The event'stimestamp fortuitously uses the same monotonic clock // time base as therest of Android. The kernel event devicedriver //(drivers/input/evdev.c) obtains timestamps using ktime_get_ts(). // ThesystemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere // callsclock_gettime(CLOCK_MONOTONIC) which is implemented as a // system call thatalso 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 isfull. Reset the pending event index // so we will try toread the device again on the next iteration. mPendingEventIndex -=1; break; } } } else { LOGW("Received unexpectedepoll event 0x%08x for device %s.", eventItem.events,device->identifier.name.string()); } } 根据设备索引的设备文件句柄,通过read函数读取input_event事件,读取个数为capacity,根据read返回值readSize除以sizeof(struct input_event)得到实际读取的事件个数,然后循环把input_event赋给event,同时capacity减去读取事件个数,如果capacity等于0,就退出循环,表明这次getEvents已经取得256个event事件了,返回给inputreader处理。如果不等于0,判断mPendingEventIndex 和 mPendingEventCount关系,如果小于继续循环从mPendingEventItems取下一个事件,如果相等,就表示事件已经取完了,执行下面的代码 if (mPendingINotify &&mPendingEventIndex >= mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; } 如果mPendingINotify为true,且mPendingEventIndex >= mPendingEventCount,就表明有设备热拔插事件发生,调用readNotifyLocked() readNotifyLocked()调用 read(mINotifyFd, event_buf, sizeof(event_buf)) 从event_buf循环取出inotify_event事件,包括设备节点创建或者删除,以及设备名字 如果是IN_CREATE,就调用openDeviceLocked打开设备,添加打开设备链表。如果是IN_DELETE,就调用closeDeviceByPathLocked关闭设备,添加关闭设备链表。并设置deviceChanged为true。 // Report added or removed devicesimmediately. if (deviceChanged) { continue; } 如果deviceChanged为true,结束本次循环,从头执行循环,即立即执行设备添加或删除。 // Return now if we have collected anyevents or if we were explicitly awoken. if (event != buffer || awoken) { break; } 如果event != buffer,就表示有event事件发生,或者awoken存在,结束循环,立即返回给inputreader处理event事件。 mPendingEventIndex = 0; int pollResult = epoll_wait(mEpollFd, mPendingEventItems,EPOLL_MAX_EVENTS, timeoutMillis); 调用epoll_wait(mEpollFd,mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,即 mPendingEventCount = size_t(pollResult); 当然,在调用epoll_wait之前,mPendingEventIndex被清0,直正的事件处理在上面的代码中。epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。 // All done, returnthe number of events we read. return event - buffer; 返回得到的event个数,支持整个getEvents已经执行完成,所有的event事件都保存在inputreader传递的RawEvent里面,看看下面的图,理解数据结构的变化 在上面2.4节最后我们看到InputReaderThread线程里面会循环调用 InputReader::loopOnce 调用 count =mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); 读取事件,上一节已经介绍了,得到事件后,接着调用 processEventsLocked(mEventBuffer, count) 处理事件 在processEventsLocked里面主要分两步处理: 1)处理来自于事件驱动设备的事件(processEventsForDeviceLocked) 2)处理设备增加、删除和修改事件—为处理事件做准备 for (const RawEvent* rawEvent = rawEvents;count;) { int32_t type = rawEvent->type; 进入for循环,取得本次循环头一个rawEvent,然后判断事件type,如果小于FIRST_SYNTHETIC_EVENT,就表示是真正的input事件,如果大于等于,就表示是input设备变化事件。 size_t batchSize = 1; if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { int32_t deviceId = rawEvent->deviceId; while (batchSize < count) { if (rawEvent[batchSize].type>= EventHubInterface::FIRST_SYNTHETIC_EVENT ||rawEvent[batchSize].deviceId != deviceId) { break; } batchSize += 1; } #if DEBUG_RAW_EVENTS LOGD("BatchSize: %d Count: %d", batchSize, count); #endif processEventsForDeviceLocked(deviceId, rawEvent, batchSize); 判断下一事件type,从rawEvent数组里面取得属于同一个设备的连续input事件,然后交给设备处理程序去处理,如果后面的事件不属于同一个设备,或者事件type是FIRST_SYNTHETIC_EVENT以后的事件,就终止查询,运行processEventsForDeviceLocked。 } else { switch (rawEvent->type) { case EventHubInterface::DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId); break; case EventHubInterface::DEVICE_REMOVED: removeDeviceLocked(rawEvent->when,rawEvent->deviceId); break; case EventHubInterface::FINISHED_DEVICE_SCAN: handleConfigurationChangedLocked(rawEvent->when); break; default: LOG_ASSERT(false); // can'thappen break; } } 如果事件type是FIRST_SYNTHETIC_EVENT以后的事件,是与Device相关的事件,这些事件是在EventHub::getEvents中产生的,并不是Kernel态的事件输入设备产生的。就调用设备添加,删除,配置变化等函数。 count -= batchSize; rawEvent += batchSize; 去掉已经处理的事件,为下一次循环做准备。 } 至此,我们看到inputreader对getEvents得到的事件都有一一对应的处理。 按照程序执行流程,应该是先有设备,然后才会有设备事件,所以先分析设备增加。 其代码如下: InputReader::addDeviceLocked String8 name =mEventHub->getDeviceName(deviceId); uint32_t classes = mEventHub->getDeviceClasses(deviceId); 得到设备名字和类型 InputDevice* device =createDeviceLocked(deviceId, name, classes); 得到一个inputreader层的device device->configure(when, &mConfig, 0); device->reset(when); 进行device配置和reset if (device->isIgnored()) { LOGI("Device added: id=%d, name='%s' (ignored non-inputdevice)", deviceId, name.string()); }else { LOGI("Device added: id=%d, name='%s', sources=0x%08x",deviceId, name.string(), device->getSources()); } 判断device的mapper是否存在,如果不存在,这个设备就不是input device mDevices.add(deviceId, device); 新建的InputDevice增加到InputReader::mDevices中 InputReader::createDeviceLocked InputDevice* device = newInputDevice(&mContext, deviceId, name, classes); 创建一个inputreader层的device // External devices. if (classes &INPUT_DEVICE_CLASS_EXTERNAL) { device->setExternal(true); } 根据类型设置是否是外部设备 接下来就是根据类型给device创建和增加事件转换器,即mapper,我们只分析touch // Touchscreens and touchpad devices. if(classes & INPUT_DEVICE_CLASS_TOUCH_MT) { device->addMapper(new MultiTouchInputMapper(device)); }else if (classes & INPUT_DEVICE_CLASS_TOUCH) { device->addMapper(new SingleTouchInputMapper(device)); } 根据多点还是单点分别创建事件转换器。我们只分析单点设备,我们的touch只有一个mapper--- SingleTouchInputMapper SingleTouchInputMapper::SingleTouchInputMapper 它继承自TouchInputMapper--- InputMapper 做一些初始化的工作 InputDevice::addMapper mMappers.add(mapper) 新建的InputMapper增加到InputDevice::mMappers 至此inputreader层的input device创建完成,并且每个device都创建了一个对应的事件转换器。 创建完就要进行配置 device->configure(when,&mConfig, 0); InputDevice::configure if (!isIgnored()) { if (!changes) { // first time only mContext->getEventHub()->getConfiguration(mId,&mConfiguration); } size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers;i++) { InputMapper* mapper = mMappers[i]; mapper->configure(when, config,changes); mSources |=mapper->getSources(); } } 判断mapper是否为空,如果不存在,就不需要配置。判断是否是配置改变,不是配置改变,那就是第一次进行配置,需要从eventhub里面得到设备的idc配置文件 接着对mapper进行配置,可能有多个事件转换器,一一对相应的mapper进行转换。 mapper->configure,我们分析的是单点touch,因此mapper对应的是SingleTouchInputMapper,它里面没有configure,继续找TouchInputMapper TouchInputMapper::configure InputMapper::configure(when, config,changes) 这个什么也没有做 if (!changes) { // first time only // Configure basic parameters. configureParameters(); // Configure common accumulators. mCursorScrollAccumulator.configure(getDevice()); mTouchButtonAccumulator.configure(getDevice()); // Configure absolute axis information. configureRawPointerAxes(); // Prepare input device calibration. parseCalibration(); resolveCalibration(); } 如果是第一次配置,就进入里面,调用 TouchInputMapper::configureParameters mParameters.gestureMode =getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) ? Parameters::GESTURE_MODE_POINTER: Parameters::GESTURE_MODE_SPOTS; 首先从驱动文件里面得到mParameters.gestureMode类型 String8gestureModeString; if(getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), gestureModeString)) { if (gestureModeString =="pointer") { mParameters.gestureMode =Parameters::GESTURE_MODE_POINTER; } else if (gestureModeString =="spots") { mParameters.gestureMode =Parameters::GESTURE_MODE_SPOTS; } else if (gestureModeString !="default") { LOGW("Invalid value fortouch.gestureMode: '%s'", gestureModeString.string()); } } 如果idc文件有touch.gestureMode存在,使用idc文件的配置 if(getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { // The device is a touch screen. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; }else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)){ // The device is a pointing device like a track pad. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; }else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { // The device is a cursor device with a touch pad attached. // By default don't use the touch pad to move the pointer. mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; }else { // The device is a touch pad of unknown purpose. mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } 从驱动文件里面得到touch的类型 String8 deviceTypeString; if(getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), deviceTypeString)) { if (deviceTypeString == "touchScreen") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; } else if (deviceTypeString == "pointer") { mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else if (deviceTypeString != "default") { LOGW("Invalid value for touch.deviceType: '%s'",deviceTypeString.string()); } } 如果idc文件有touch.deviceType存在,使用idc文件的配置,这里我们配置是touchScreen,即mParameters.deviceType =Parameters::DEVICE_TYPE_TOUCH_SCREEN mParameters.orientationAware= mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), mParameters.orientationAware); 从idc文件里面得到mParameters.orientationAware的值 mParameters.associatedDisplayId= -1; mParameters.associatedDisplayIsExternal =false; if (mParameters.orientationAware || mParameters.deviceType ==Parameters::DEVICE_TYPE_TOUCH_SCREEN || mParameters.deviceType ==Parameters::DEVICE_TYPE_POINTER) { mParameters.associatedDisplayIsExternal= mParameters.deviceType ==Parameters::DEVICE_TYPE_TOUCH_SCREEN && getDevice()->isExternal(); mParameters.associatedDisplayId = 0; } 根据mParameters.deviceType和getDevice()->isExternal来判断是否使用外部显示配置。在eventhub里面我们的触摸屏是usb bus,被配置成外部设备,触摸屏配置成DEVICE_TYPE_TOUCH_SCREEN,因此mParameters.associatedDisplayIsExternal等于1,及使用外部的显示配置。 至此TouchInputMapper::configureParameters配置完成 // Configure common accumulators. mCursorScrollAccumulator.configure(getDevice()); mTouchButtonAccumulator.configure(getDevice()); 配置光标和按键加速,都是根据驱动文件或者idc文件,这个都不需要。 // Configure absolute axisinformation. configureRawPointerAxes(); 配置原始信息,它先调用TouchInputMapper::configureRawPointerAxes mRawPointerAxes.clear()先将 mRawPointerAxes清除干净 接着调用SingleTouchInputMapper::configureRawPointerAxes getAbsoluteAxisInfo(ABS_X,&mRawPointerAxes.x); getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); getAbsoluteAxisInfo(ABS_TILT_Y,&mRawPointerAxes.tiltY); InputMapper::getAbsoluteAxisInfo调用 getEventHub()->getAbsoluteAxisInfo从驱动文件里面得到需要参数 // Prepare input device calibration. parseCalibration(); resolveCalibration(); 根据idc文件配置校正参数。 if (!changes || (changes &InputReaderConfiguration::CHANGE_POINTER_SPEED)) { // Update pointer speed. mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); } 如果是第一次配置或者是改变速度,需要update pointer speed bool resetNeeded = false; if (!changes ||(changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO |InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) { // Configuredevice sources, surface dimensions, orientation and // scalingfactors. configureSurface(when, &resetNeeded); } 如果是第一次配置或者是显示等改变,需要调用configureSurface TouchInputMapper::configureSurface // Determine device mode. if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER &&mConfig.pointerGesturesEnabled) { mSource = AINPUT_SOURCE_MOUSE; mDeviceMode = DEVICE_MODE_POINTER; } else if (mParameters.deviceType ==Parameters::DEVICE_TYPE_TOUCH_SCREEN && mParameters.associatedDisplayId>= 0) { mSource = AINPUT_SOURCE_TOUCHSCREEN; mDeviceMode = DEVICE_MODE_DIRECT; } else { mSource = AINPUT_SOURCE_TOUCHPAD; mDeviceMode = DEVICE_MODE_UNSCALED; } 根据mParameters.deviceType决定mSource和mDeviceMode // Ensure we have valid X and Y axes. if(!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { LOGW(INDENT"Touch device '%s' did not report support for X or Y axis! " "Thedevice will be inoperable.", getDeviceName().string()); mDeviceMode =DEVICE_MODE_DISABLED; return; } 判断x和y参数是否有效,这里就是判断触摸屏x和y的坐标范围的,在eventhub里面,只要最大和最小不相等,就是有效的。如果无效,设备模式就是关闭的,不能使用。 // Get associated display dimensions. if(mParameters.associatedDisplayId >= 0) { if(!mConfig.getDisplayInfo(mParameters.associatedDisplayId, mParameters.associatedDisplayIsExternal, &mAssociatedDisplayWidth, &mAssociatedDisplayHeight, &mAssociatedDisplayOrientation)) { LOGI(INDENT"Touch device '%s' could not query the properties of its associated " "display %d. The device willbe inoperable until the display size " "becomes available.", getDeviceName().string(), mParameters.associatedDisplayId); mDeviceMode =DEVICE_MODE_DISABLED; return; } } 根据associatedDisplayId和associatedDisplayIsExternal得到显示屏的分辨率,associatedDisplayId在configureParameters里面设为0,associatedDisplayIsExternal根据触摸屏类型和bus设为1. 调用InputReaderConfiguration::getDisplayInfo得到设置好的surface size。 if(displayId == 0) { const DisplayInfo& info = external? mExternalDisplay : mInternalDisplay; if (info.width > 0 &&info.height > 0) { if (width) { *width = info.width; } if (height) { *height = info.height; } if (orientation) { *orientation =info.orientation; } return true; } } 可以看到android4.0里面,分内部和外部分辨率两种。如果info里面都是0,这个函数返回false,就表示android设备还没有走到设置surface size这一步,就会打印提示信息,稍后android启动里面就会执行设置surface size的程序。 // Configure dimensions. int32_t width, height,orientation; if (mDeviceMode ==DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { width =mAssociatedDisplayWidth; height =mAssociatedDisplayHeight; orientation =mParameters.orientationAware ? mAssociatedDisplayOrientation : DISPLAY_ORIENTATION_0; } else { width =mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1; height =mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1; orientation =DISPLAY_ORIENTATION_0; } 根据mDeviceMode类型设置宽和高参数,根据上面的分析宽和高就是取自显示屏的分辨率。 bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight !=height; if (sizeChanged ||deviceModeChanged) { LOGI("Devicereconfigured: id=%d, name='%s', surface size is now %dx%d, mode is %d", getDeviceId(), getDeviceName().string(), width, height, mDeviceMode); mSurfaceWidth =width; mSurfaceHeight =height; // Configure X andY factors. mXScale =float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1); mYScale = float(height) / (mRawPointerAxes.y.maxValue -mRawPointerAxes.y.minValue + 1); mXPrecision = 1.0f/ mXScale; mYPrecision = 1.0f/ mYScale; 如果显示屏分辨率不等于开始保存的,就需要重新设置一些参数。红色部分就是显示屏分辨率和触摸屏坐标范围得到的转换因子,使用这个转换因子就可以把触摸屏坐标转换成屏幕坐标。 接着下面是根据校准参数配置校准因子的。这里我们不使用这种方式,所以不执行 下面如果sizeChanged改变,重新配置一些参数,同时设置 *outResetNeeded = true; 至此configureSurface执行完成,与触摸屏坐标有关的配置也完成了。 if (changes && resetNeeded) { // Send reset, unless this is the firsttime the device has been configured, // in which case the reader will callreset itself after all mappers are ready. getDevice()->notifyReset(when); } 如果有改变而且需要reset,reader将reset自己 至此Input Device的configure和mapper configure都已完成完成 配置完成就要进行初始化 device->reset(when); 调用InputDevice::reset mapper->reset(when) mapper初始化 至此整个InputReader::addDeviceLocked已经分析完成了,到了这一步,我们的整个input系统都已经准备好去接收真正的input event并处理。 分析设备删除, 其代码如下: void InputReader::removeDeviceLocked(nsecs_twhen, int32_t deviceId) { InputDevice* device = NULL; ssize_t deviceIndex =mDevices.indexOfKey(deviceId); if (deviceIndex >= 0) { device = mDevices.valueAt(deviceIndex); mDevices.removeItemsAt(deviceIndex, 1); 把设备从mDevices链表里面移除 } else { LOGW("Ignoring spurious deviceremoved event for deviceId %d.", deviceId); return; } if (device->isIgnored()) { LOGI("Device removed: id=%d,name='%s' (ignored non-input device)", device->getId(),device->getName().string()); } else { LOGI("Device removed: id=%d,name='%s', sources=0x%08x", device->getId(), device->getName().string(),device->getSources()); } device->reset(when); 清除device配置 delete device; 删除device } InputReader::processEventsLocked设备增加、删除处理总结: 它负责处理inputreader层Device 增加、删除事件。增加事件的流程为:为一个新增的Device创建一个InputDevice,并增加到InputReader::mDevices中;根据新增加设备的class类别,创建对应的消息转换器(InputMapper),然后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。 EventHub与InputReader各自管理功能: l EventHub管理一堆Device,每一个Device与Kernel中一个事件输入设备对应 l InputReader管理一堆InputDevice,每一个InputDevice与EventHub中的Device对应 l InputDevice管理一些与之相关的InputMapper,每个device类型不同,会有一个InputMapper或者多个InputMapper,如我们touch只有:SingleTouchInputMapper。 下面再来看看inputreader里面touch类的关系 上一节讲到input device的touch mapper配置时,会得到surface size,如果得到为0,就会把mDeviceMode配置为DEVICE_MODE_DISABLED,表示这个设备暂时无法使用,因此即使我们的input系统都准备好了,但是touch还是无法使用。下面讲如何配置surface size。 在2.3节线程创建里面看到: SystemServer.java(frameworks\base\services\java\com\android\server)里面 ServerThread::run调用 Slog.i(TAG,"Window Manager"); wm = WindowManagerService.main(context, power, factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot); ServiceManager.addService(Context.WINDOW_SERVICE, wm); 下面接着就会调用 try{ wm.displayReady(); wm就是上面创建的,为后面整个显示做准备 } catch (Throwable e) { reportWtf("making display ready", e); } WindowManagerService.java(frameworks\base\services\java\com\android\server\wm) 里面 displayReady里面调用 WindowManager wm =(WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mDisplay = wm.getDefaultDisplay(); mInitialDisplayWidth =mDisplay.getRawWidth(); mInitialDisplayHeight = mDisplay.getRawHeight(); 得到原始显示分辨率,这时就是整个屏幕的分辨率 mInputManager.setDisplaySize(Display.DEFAULT_DISPLAY, mDisplay.getRawWidth(),mDisplay.getRawHeight(), mDisplay.getRawExternalWidth(), mDisplay.getRawExternalHeight()); mInputManager是在WindowManagerService一开始就创建的,我们来看看setDisplaySize里面后四个参数: publicint getRawWidth() { int w = getRawWidthNative(); if (DEBUG_DISPLAY_SIZE) Slog.v( TAG, "Returning rawdisplay width: " + w); return w; } private native int getRawWidthNative(); publicint getRawHeight() { int h = getRawHeightNative(); if (DEBUG_DISPLAY_SIZE) Slog.v( TAG, "Returning rawdisplay height: " + h); return h; } private native int getRawHeightNative(); 这两个函数要调用native空间函数 android_view_Display.cpp(frameworks\base\core\jni) staticjint android_view_Display_getRawWidthNative( JNIEnv* env, jobject clazz) { DisplayID dpy = env->GetIntField(clazz,offsets.display); return SurfaceComposerClient::getDisplayWidth(dpy); } staticjint android_view_Display_getRawHeightNative( JNIEnv* env, jobject clazz) { DisplayID dpy = env->GetIntField(clazz,offsets.display); returnSurfaceComposerClient::getDisplayHeight(dpy); } 可以看到通过surface的client端得到屏幕的分辨率。 如何得到外部分辨率 publicint getRawExternalWidth() { return 1280; } /** * If the display is mirrored to anexternal HDMI display, returns the * height of that display. * @hide */ public int getRawExternalHeight() { return 720; } 可以看到如果设备外界hdmi显示,就用1280*720分辨率,这里有个疑问:如果客户设备外的是1920*1080的hdmi,那么这两个值是否需要变化?而且看这两个函数用处,只有触摸坐标转换和HeightReceiver.java使用,如果使用外部分辨率,那么android显示系统是如何知道的? 继续看mInputManager.setDisplaySize InputManager.java(frameworks\base\services\java\com\android\server\wm) 里面 setDisplaySize调用 nativeSetDisplaySize(displayId,width, height, externalWidth, externalHeight); 从下面开始就进入native空间 com_android_server_InputManager.cpp(frameworks\base\services\jni)里面 nativeSetDisplaySize对应android_server_InputManager_nativeSetDisplaySize调用 gNativeInputManager->setDisplaySize(displayId,width, height, externalWidth, externalHeight); 在setDisplaySize里面判断 if (mLocked.displayWidth != width || mLocked.displayHeight !=height) { changed = true; mLocked.displayWidth = width; mLocked.displayHeight = height; sp if (controller != NULL) { controller->setDisplaySize(width, height); } } if (mLocked.displayExternalWidth != externalWidth ||mLocked.displayExternalHeight != externalHeight) { changed = true; mLocked.displayExternalWidth = externalWidth; mLocked.displayExternalHeight = externalHeight; } 如果这次设置的值和旧值相等,就什么也不做退出。如果不相等,设置changed = true,同时保存新的值 if(changed) { mInputManager->getReader()->requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } 如果值有变化,就调用inputreader刷新配置,提示是displayinfo改变 首先是去InputManager.cpp(frameworks\base\services\input) 文件里面 getReader() returnmReader; 这个就是InputManager创建是创建的inputreader InputReader.cpp (frameworks\base\services\input)里面 InputReader::requestRefreshConfiguration调用 if (changes) { bool needWake =!mConfigurationChangesToRefresh; mConfigurationChangesToRefresh |= changes; if (needWake) { mEventHub->wake(); } } 如果改变类型不为0,就mConfigurationChangesToRefresh取反送给needWake,mConfigurationChangesToRefresh表示需要改变配置的类型集合,初始化为0,因此needWake就为1,同时把改变类型与给mConfigurationChangesToRefresh,接着判断needWake,如果为1,就进入mEventHub的唤醒程序。 这段话意思就是如果有正在改变配置需求,就表明整个input系统正在运行,所以不需要唤醒。只需要把新的改变类型放在mConfigurationChangesToRefresh就行了,如果没有,那么input系统有可能在睡眠,为了快速响应改变,需要唤醒整个input系统。 EventHub.cpp(frameworks\base\services\input)里面 EventHub::wake()调用 nWrite = write(mWakeWritePipeFd, "W",1); 直接向mWakeWritePipeFd管道里面写一个字符。前面2.5节讲到mWakeReadPipeFd已经被mEpollFd监控了,向mWakeWritePipeFd写就会引起mWakeReadPipeFd变化。在EventHub::getEvents里面就会执行if (eventItem.data.u32 == EPOLL_ID_WAKE)这个分支,设置awoken为true,当mPendingEventItems事件处理完,就会判断awoken,如果为true就立即结束循环,返回给inputreader进行处理。 至此,配置surface size执行部分结束了,但是size并没有真正配置到mExternalDisplay和mInternalDisplay里面,只是改变类型放在mConfigurationChangesToRefresh里面,真正的size还保存在NativeInputManager的mLocked里面 这时并没有输入事件或者设备变化发生,因此InputReader::loopOnce很快结束,进入下一次循环 InputReader::loopOnce 接着调用 uint32_t changes =mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; refreshConfigurationLocked(changes); } InputReader::refreshConfigurationLocked mPolicy->getReaderConfiguration(&mConfig); mPolicy就是NativeInputManager的对象,在创建inputreader传入的。 com_android_server_InputManager.cpp(frameworks\base\services\jni)里面 NativeInputManager::getReaderConfiguration调用 { // acquire lock AutoMutex _l(mLock) outConfig->setDisplayInfo(0, false/*external*/, mLocked.displayWidth,mLocked.displayHeight, mLocked.displayOrientation); outConfig->setDisplayInfo(0, true/*external*/, mLocked.displayExternalWidth,mLocked.displayExternalHeight, mLocked.displayOrientation); } // release lock outConfig就是inputreader里面的mConfig,因此调用 InputReaderConfiguration::setDisplayInfo if (displayId == 0) { DisplayInfo& info = external ?mExternalDisplay : mInternalDisplay; info.width = width; info.height = height; info.orientation = orientation; } 看到没有,饶了一个大圈,这里才把surface size真正放在mExternalDisplay和 mInternalDisplay里面,供后面调用InputReaderConfiguration::getDisplayInfo时使用。 refreshConfigurationLocked函数会在inputreader创建时执行一次,但那个时候input device还没有创建,而且changes为0,因此不会执行下面的部分。 if (changes &InputReaderConfiguration::CHANGE_MUST_REOPEN) { mEventHub->requestReopenDevices(); 如果改变类型是reopen,就调用eventhub的requestReopenDevices EventHub::requestReopenDevices里面设置mNeedToReopenDevices = true,这个会在EventHub::getEvents里面进行判断,前面2.6节已经讲了 } else { for (size_t i = 0; i InputDevice* device =mDevices.valueAt(i); device->configure(now,&mConfig, changes); } } 得到所有的device,循环调用每个device的configure去重新配置。 InputDevice::configure已经在InputReader::addDeviceLocked讲过了,刚开始第一次配置changes是0,表示需要全面的初始化。现在只需要配置改变的部分了。 至此,我们整个inputtouch的工作环境已经配置好了,就等有触摸事件发生然后处理了 InputReader::processEventsForDeviceLocked,它负责处理来自于同一个设备且在mEventBuffer中连续的多个事件,其函数原型如下: ssize_t deviceIndex =mDevices.indexOfKey(deviceId); if (deviceIndex < 0) { LOGW("Discarding event for unknowndeviceId %d.", deviceId); return; } 得到发生事件设备索引 InputDevice*device = mDevices.valueAt(deviceIndex); 根据索引得到发生事件的device if (device->isIgnored()) { //LOGD("Discarding event forignored deviceId %d.", deviceId); return; } 如果device没有mapper,就返回不做任何处理。 device->process(rawEvents,count); 调用process处理 InputDevice::process for(const RawEvent* rawEvent = rawEvents; count--; rawEvent++) 一次取出每一个事件 for(size_t i = 0; i < numMappers; i++) { InputMapper* mapper =mMappers[i]; mapper->process(rawEvent); } 对每一个事件都用这个device所有mapper进行处理 从上面的代码中可以看出,在InputDevice::process中,对于传入的每一个RawEvent,依次调用InputDevice中的每一个InputMapper来进行处理。前面提到过,InputDevice包含一组处理对应设备事件InputMapper,现在这些InputMapper开始干活了。 因为我们的touch只有一个SingleTouchInputMapper 这里先说说单点touch需要处理事件集合 代码: input_report_abs(myInputDev, ABS_X, event->x); 产生的事件:*type, code, value 代码: input_report_key(myInputDev,BTN_TOUCH, 1); 代码: input_sync(myInputDev); SingleTouchInputMapper::process调用 TouchInputMapper::process(rawEvent); TouchInputMapper::process mCursorButtonAccumulator.process(rawEvent); 因为是touch,rawEvent->type 是 EV_KEY,但是rawEvent->scanCode不匹配里面任何值,不起任何作用 mCursorScrollAccumulator.process(rawEvent); 因为是touch,rawEvent->type是 EV_KEY,不是EV_REL,不起任何作用 mTouchButtonAccumulator.process(rawEvent); TouchButtonAccumulator::process if(rawEvent->type == EV_KEY) { switch (rawEvent->scanCode) { case BTN_TOUCH: mBtnTouch = rawEvent->value; break; 可以看到把BTN_TOUCH的值放在mBtnTouch里面 接着处理坐标信息: mSingleTouchMotionAccumulator.process(rawEvent) SingleTouchMotionAccumulator::process caseABS_X: mAbsX = rawEvent->value; break; case ABS_Y: mAbsY = rawEvent->value; break; 将坐标信息保存在mAbsX和mAbsY里面 当BTN_TOUCH,ABS_X和ABS_Y处理完,接下来就会处理EV_SYN事件 在TouchInputMapper::process里面, if(rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { sync(rawEvent->when); } TouchInputMapper::sync调用 syncTouch(when,&havePointerIds) SingleTouchInputMapper::syncTouch if(mTouchButtonAccumulator.isToolActive()) { 判断BTN_TOUCH是否等于1,即是否有touchdown,如果有进入下面处理 mCurrentRawPointerData.pointerCount = 1;设置触摸点数1 mCurrentRawPointerData.idToIndex[0] = 0; 触摸点索引为0 RawPointerData::Pointer& outPointer =mCurrentRawPointerData.pointers[0]; outPointer.id = 0; outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); outPointer.pressure =mSingleTouchMotionAccumulator.getAbsolutePressure(); outPointer.touchMajor = 0; outPointer.touchMinor = 0; outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); outPointer.toolMinor =mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); outPointer.orientation = 0; outPointer.distance =mSingleTouchMotionAccumulator.getAbsoluteDistance(); outPointer.tiltX =mSingleTouchMotionAccumulator.getAbsoluteTiltX(); outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); outPointer.toolType = mTouchButtonAccumulator.getToolType(); if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; } outPointer.isHovering = isHovering; 把相关的信息放在mCurrentRawPointerData.pointers[0]里面,这里主要是x,y坐标 TouchInputMapper::sync继续处理 //Reset state that we will compute below. mCurrentFingerIdBits.clear(); mCurrentStylusIdBits.clear(); mCurrentMouseIdBits.clear(); mCurrentCookedPointerData.clear(); 这几个清零,后面填入相应的值 if (mDeviceMode== DEVICE_MODE_DISABLED) { // Drop all input if the device isdisabled. mCurrentRawPointerData.clear(); mCurrentButtonState = 0; } 如果设备状态是关闭的,就把mCurrentRawPointerData保存的数据清除,返回 uint32_t policyFlags = 0; bool initialDown =mLastRawPointerData.pointerCount == 0 &&mCurrentRawPointerData.pointerCount != 0; bool buttonsPressed =mCurrentButtonState & ~mLastButtonState; if (initialDown || buttonsPressed) { // If this is a touch screen, hidethe pointer on an initial down. if (mDeviceMode ==DEVICE_MODE_DIRECT) { getContext()->fadePointer(); } // Initial downs on external touchdevices should wake the device. // We don't do this for internaltouch screens to prevent them from waking // up in your pocket. // TODO: Use the input deviceconfiguration to control this behavior more finely. if (getDevice()->isExternal()) { policyFlags |=POLICY_FLAG_WAKE_DROPPED; } } 判断是不是第一次按下,如果是,Ifthis is a touch screen, hide the pointer on an initial down。 如果是外部设备,就唤醒整个系统,如果是内部设备,就不用唤醒,注释写的很清楚,有可能放在口袋里面误触摸唤醒系统 // Consume rawoff-screen touches before cooking pointer data. // If touches are consumed, subsequentcode will not receive any pointer data. if (consumeRawTouches(when,policyFlags)) { mCurrentRawPointerData.clear(); 如果是唤醒设备的点击,就把mCurrentRawPointerData清零,只需要唤醒设备就行了。 cookPointerData(); 进行触摸坐标到原始坐标的转换 TouchInputMapper::cookPointerData 首先进行一系列的坐标校准,接下来进行坐标转换 //X and Y // Adjust coords for surface orientation. float x, y; switch (mSurfaceOrientation) { case DISPLAY_ORIENTATION_90: x = float(in.y -mRawPointerAxes.y.minValue) * mYScale; y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale; orientation -= M_PI_2; if (orientation < - M_PI_2) { orientation += M_PI; } break; case DISPLAY_ORIENTATION_180: x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale; y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale; break; case DISPLAY_ORIENTATION_270: x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale; y = float(in.x - mRawPointerAxes.x.minValue) * mXScale; orientation += M_PI_2; if (orientation > M_PI_2) { orientation -= M_PI; } break; default: x = float(in.x - mRawPointerAxes.x.minValue) * mXScale; y = float(in.y - mRawPointerAxes.y.minValue) * mYScale; break; } 在TouchInputMapper::configureSurface里面 通过mConfig.getDisplayInfo(mParameters.associatedDisplayId, mParameters.associatedDisplayIsExternal, &mAssociatedDisplayWidth,&mAssociatedDisplayHeight, &mAssociatedDisplayOrientation)) 得到mAssociatedDisplayOrientation的值,这个值是通过setDisplayInfo从mLocked.displayOrientation得到的。在NativeInputManager创建是初始化这个值 mLocked.displayOrientation =DISPLAY_ORIENTATION_0 orientation = mParameters.orientationAware? mAssociatedDisplayOrientation :DISPLAY_ORIENTATION_0; mParameters.orientationAware是idc文件里面的值,我们这里是1,即 orientation = mAssociatedDisplayOrientation默认为DISPLAY_ORIENTATION_0 bool orientationChanged =mSurfaceOrientation != orientation; if (orientationChanged) { mSurfaceOrientation = orientation; } mSurfaceOrientation = orientation = mAssociatedDisplayOrientation= mLocked.displayOrientation 因此在cookPointerData会执行 x = float(in.x - mRawPointerAxes.x.minValue) * mXScale; y = float(in.y - mRawPointerAxes.y.minValue) * mYScale; 这就是坐标转换的地方,in.x和in.y是触摸屏坐标,mRawPointerAxes.x.minValue和mRawPointerAxes.y.minValue是触摸屏坐标范围最小值,mXScale和mYScale就是 mXScale= float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1); mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue+ 1); 把它们和在一起就是http://source.android.com/tech/input/touch-devices.html这个里面说的: For a touch screen, the systemautomatically interpolates the reported touch positions in surface units toobtain touch positions in display pixels according to the followingcalculation: 接着把转换后的坐标放在out里面 PointerCoords& out =mCurrentCookedPointerData.pointerCoords[i]; out.clear(); out.setAxisValue(AMOTION_EVENT_AXIS_X, x); out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); dispatchTouches(when, policyFlags) 调用 if(currentIdBits == lastIdBits) 如果当前点id和上一次id相同,表明这是个移动事件,不是的话就判断是down,up或者move,然后调用dispatchMotion,它的第四个参数就是down,up,move等类型 在dispatchMotion中,根据cooked数据创建NotifyMotionArg对象,它描述了一个移动事件,接着调用TouchInputMapper::getListener()->notifyMotion(&args) TouchInputMapper::getListener()调用mContext->getListener(),此mContext为InputReader::mContext,所以其getListener()返回的则为InputReader::mQueuedListener,则最后调用QueuedInputListener::notifyMotion QueuedInputListener::notifyMotion(constNotifyMotionArgs* args) { mArgsQueue.push(newNotifyMotionArgs(*args)); } 把传递过来的NotifyMotionArg参数复制一份,然后加入QueuedInputListener::mArgsQueue例表中 补充1)InputReader::mContext在构造时用自己的指针初始化了mContext,从而mContext::mReader则为此InputReader实例。 dispatchHoverEnterAndMove(when,policyFlags); 调用dispatchMotion 至此整个processEventsLocked处理流程结束,已经把来自于事件设备的事件处理之后放入到各种NotifyArgs(如NotifyMotionArgs)之中,然后把这些各种NotifyArgs加入InputReader::mQueuedListener::mArgsQueue链表中。接着InputReader::loopOnce调用 mQueuedListener->flush() Flush函数就是要把mArgsQueue中的所有NotifyArgs进行处理。 voidQueuedInputListener::flush() { size_t count = mArgsQueue.size(); for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; args->notify(mInnerListener); delete args; } mArgsQueue.clear(); } 调用链表中每个NotifyArgs的notify函数,且有一个有意思的参数 mInnerListener,这个参数在前面多次提到过,它是在创建mQueuedListener时提供的,它其实就是InputManager中的mDispatcher,前面一直在InputReader中打转转,现在终于看到InputDispatcher登场了,说明事件很快就可以谢幕了。 再向下看一下吧,这么多类NotifyArgs,为描述方便,下面以NotifyMotionArgs为例,其代码为: voidNotifyMotionArgs::notify(const sp listener->notifyMotion(this); } 下面就看看InputDispatcher(mDispatcher)的notifyMotion函数做了些什么。这个InputDispatcher::notifyMotion(constNotifyMotionArgs* args)可就不简单了。 在InputDispatcher::notifyMotion中, 2)对于AMOTION_EVENT_ACTION_MOVE事件,则从mInboundQueue队列里面寻找到对应的entry,把args信息放在这个entry里面 3)对于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,则直接根据NotifyMotionArgs提供的信息,构造一个MotionEntry。 4)调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue中,并返回是否需要唤醒mLooper<向pipe中写入数据>的标识。 if(needWake) { mLooper->wake(); } 根据表示唤醒mLooper 以上操作都是在InputReader线程中完成的,现在应该InputDispatcher线程开始工作了。 至此InputReader::loopOnce一次循环结束,所有的input事件已经处理并放在了InputDispatcher::mInboundQueue里面 事件处理相关数据结构如下图所示: 至此的消息结构变化流程: 前面线程启动提到InputDispatcher::dispatchOnce调用 mLooper->pollOnce(timeoutMillis); 其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据情况调用mLooper->wake)。 其调用流程如下: mLooper->pollOnce(inttimeoutMillis)-> Looper.cpp(frameworks\base\libs\utils) Looper::pollOnce(inttimeoutMillis, int* outFd, int* outEvents, void** outData) 如果没有事件输入,那么InputDispatcher::dispatchOnce 就会被阻塞在pollOnce,调用mLooper->wake唤醒。就会重新执行dispatchOnce,就会调用dispatchOnceInnerLocked InputDispatcher::dispatchOnceInnerLocked 1)从mInboundQueue从中依次取出EventEntry 2)调用InputDispatcher::dispatchMotionLocked处理此MotionEntry 3)调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked 对于InputDispatcher::mCurrentInputTargets中的每一个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked, InputDispatcher::dispatchEventToCurrentInputTargetsLocked for (size_t i = 0; i< mCurrentInputTargets.size(); i++) { const InputTarget& inputTarget =mCurrentInputTargets.itemAt(i); ssize_t connectionIndex =getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget, resumeWithAppendedMotionSample); } else { #if DEBUG_FOCUS LOGD("Dropping event deliveryto target with channel '%s' because it " "is no longerregistered with the input dispatcher.", inputTarget.inputChannel->getName().string()); #endif } } InputDispatcher::prepareDispatchCycleLocked 1)调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。 2)调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中 3)调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中,调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧! 请参考网上的linux内核input子系统解析看下面的图: 这里只简单说一下驱动里面触摸屏事件注册 input_dev->evbit[0]= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)]= BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev,ABS_X, 0, 32767, 0, 0); input_set_abs_params(input_dev,ABS_Y, 0, 32767, 0, 0); input_register_device(input_dev); 事件发送 input_report_key(usbtouch->dev,BTN_TOUCH, touch); input_report_abs(usbtouch->dev,ABS_X, x); input_report_abs(usbtouch->dev,ABS_Y, y); input_sync(usbtouch->dev);2.7处理事件
2.7.1 处理事件准备—设备添加删除
2.7.2 处理事件准备—设置surface size
2.7.3处理来自于事件驱动设备的事件
input_report_abs(myInputDev, ABS_Y, event->y);
EV_ABS,ABS_X,event->x
EV_ABS,ABS_Y,event->y
产生的事件:*type, code, value
EV_KEY, BTN_TOUCH,1
它调用input_event(dev,EV_SYN, SYN_REPORT, 0);
产生的事件:*type, code, value
EV_SYN, SYN_REPORT, 0displayX = (x - minX) * displayWidth / (maxX - minX + 1)
displayY = (y - minY) * displayHeight / (maxY - minY + 1)
补充2)在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMapper的mContext中。
1)根据NotifyMotionArgs提供的信息,构造一个MotionEvent,再调用mPolicy->filterInputEvent看是否需要丢弃此事件,如果需要丢弃则马上返加。其中mPolicy为NativeInputManager实例,在构造InputDispatcher时提供的参数。2.8分发事件
3 内核层驱动