在 Android(安卓)4.1上,分析 input -- android framework 部分 2
Androidinouttouch流程分两部分:
一部分是从androidframework开始,如何读取touch设备的事件并分发。
另一部分是从linux内核开始,如何从触摸屏读取触摸坐标并送给touch设备。
2androidframework层
2.1文件结构
首先看看EventInput文件结构吧,在frameworks/base/services/input/ 之下
2.2模块介绍
Eventhub
它是系统中所有事件的 中央处理站。它管理所有系统中可以识别的输入设备的输入事件,此外,当设备增加或删除时,EventHub将产生相应的输入事件给系统。EventHub通过 getEvents函数,给系统提供一个输入事件流。它也支持查询输入设备当前的状态(如哪些键当前被按下)。而且EventHub还跟踪每个输入调入的能力,比如 输入设备的类别,输入设备支持哪些按键。
InputReader
InputReader从EventHub中读取原始事件数据(RawEvent),并由各个InputMapper 处理之后输入对应的inputlistener;InputReader拥有一个InputMapper集合。它做的大部分工作在 InputReader线程中 完成,但是InputReader可以接受任意线程的查询。为了可管理性,InputReader使用一个简单的 Mutex 来保护它的状态。InputReader拥有一个EventHub对象,但这个对象不是它创建的,而是在创建InputReader时作为参数传入的。
InputDispatcher
InputDispatcher负责把事件分发给输入目标,其中的一些功能(如识别输入目标)由独立的 policy对象控制。
InputManager
InputManager是系统事件处理的核心,它虽然不做具体的事,但管理工作还是要做的,比如接受我们客户的投诉和索赔要求,或者老板的出气筒。
InputManager使用两个线程:
1)InputReaderThread 叫做"InputReader"线程,它负责读取 并预处理RawEvent,appliespolicy把消息送入DispatcherThead管理的队列中。
2)InputDispatcherThread 叫做"InputDispatcher"线程,它在队列上等待新的输入事件,并且异步地把这些事件 分发给应用程序。
InputReaderThread类与InputDispatcherThread类 不共享内部状态,所有的通信都是单向的,从InputReaderThread 到 InputDispatcherThread。两个类可以通过InputDispatchPolicy进行交互。
InputManager类从不与Java交互,而InputDispatchPolicy 负责执行 所有与系统的外部交互,包括调用DVM业务。
看看下图理解input下面几个模块的关系
2.3线程创建
SystemServer大家熟悉吧,它是androidinit进程启动的,它的任务就是启动android里面很多服务,并管理起来,如果大家不熟悉,请参考andorid启动流程分析
SystemServer.java(frameworks\base\services\java\com\android\server)里面
ServerThread::run调用
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)里面
WindowManagerServicemain 调用
WMThreadthr=newWMThread(context,pm,haveInputMethods,allowBootMsgs);
thr.start(); 接着调用
WMThread::run调用
WindowManagerServices=newWindowManagerService(mContext,mPM, mHaveInputMethods,mAllowBootMessages);
接着在WindowManagerService里面调用
mInputManager=newInputManagerService(context,this);
至此我们创建了一个java层input设备管理器;
InputManagerService.java(frameworks\base\services\java\com\android\server\input)里面
InputManagerService调用
nativeInit(this, mContext, mHandler.getLooper().getQueue());
从下面开始就进入native空间
com_android_server_input_InputManagerService.cpp(frameworks\base\services\jni)里面
nativeInit对应 nativeInit调用
NativeInputManager* im =newNativeInputManager(contextObj,callbacksObj,messageQueue->getLooper());
在NativeInputManager里面调用
sp<EventHub>eventHub=newEventHub();
mInputManager=newInputManager(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=newInputReader(eventHub,readerPolicy,mDispatcher);
initialize();
它创建了InputDispatcher对象,同时也创建了InputReader对象。并分别暂存于mDispatcher和mReader变量中。注意eventHub 和 mDispatcher都作为参数创建InputReader对象。后面还用initialize来初始化。下面是initialize函数的定义:
voidInputManager::initialize(){
mReaderThread=newInputReaderThread(mReader);
mDispatcherThread=newInputDispatcherThread(mDispatcher);
}
它创建两个线程对象,一个是InputReaderThread线程对象,负责input事件的获取;另一个是InputDispatcherThread线程对象,负责input消息的发送,这两个类 都是 继承 Thread类。
(注:以上两个线程对象都有自己的threadLoop函数,它将在Thread::_threadLoop中被调用,这个Thread::_threadLoop是线程入口函数,线程在Thread::run中被真正地创建)
InputDispatcher.cpp(frameworks\base\services\input)里面
InputDispatcher::InputDispatcher做一些准备工作
InputReader.cpp(frameworks\base\services\input)里面
InputReader::InputReader做一些准备工作
==================================
2.4 线程启动
在上面讲到在WindowManagerService里面调用
mInputManager = new InputManagerService(context, mInputMonitor);
创建input管理器,紧接着调用
mInputManager.start();
InputManagerService.java(frameworks\base\services\java\com\android\server\input)里面start调用
nativeStart();
从下面开始就进入native空间
com_android_server_input_InputManagerService.cpp(frameworks\base\services\jni)里面
nativeStart对应nativeStart调用
status_t result = im->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调用
size_t count =mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);// 得到input输入事件
processEventsLocked(mEventBuffer, count);//处理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机制实现外界事件请求作出响应。
至此整个androidinputevent框架已经运转起来了,好像到现在还没有提到touch,别着急,且看下面的分析;
==========================
2.5event初始化
还记得nativeInit里面创建
sp<EventHub>eventHub=newEventHub();
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,用于监控/dev/input/ 目录下 删除 和 创建设备节点的事件
mINotifyFd=inotify_init();
intresult=inotify_add_watch(mINotifyFd,DEVICE_PATH,IN_DELETE|IN_CREATE);
//将mINotifyFd注册到mEpollFd里面,通过epoll来监听mINotifyFd的变化
result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mINotifyFd,&eventItem)
int wakeFds[2];
result=pipe(wakeFds);
mWakeReadPipeFd=wakeFds[0];
mWakeWritePipeFd=wakeFds[1];
//创建唤醒管道,并设置为非阻塞,如果向mWakeWritePipeFd写,那么mWakeReadPipeFd就会有变化
result=fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK)
result=fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK)
//将mWakeReadPipeFd注册到mEpollFd里面,通过epoll来监听mWakeReadPipeFd的变化
eventItem.data.u32=EPOLL_ID_WAKE;
result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
至此EventHub对象以及构造完成了,mEpollFd监听mINotifyFd 和mWakeReadPipeFd 的变化。
2.6读取事件
在上面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(;;){进入for循环
nsecs_tnow=systemTime(SYSTEM_TIME_MONOTONIC);
//Reopeninputdevicesifneeded.
if(mNeedToReopenDevices){
mNeedToReopenDevices=false;
LOGI("Reopeningallinputdevicesduetoaconfigurationchange.");
closeAllDevicesLocked();
mNeedToScanDevices=true;
break;//returntothecallerbeforeweactuallyrescan
}
检查mNeedToReopenDevices是否为ture,如果为true,在closeAllDevicesLocked里面,关闭所有打开的硬件设备描述符,并把需要删除的设备放在mClosingDevices链表里面,如果这个设备是在mOpeningDevices里面,就忽略跳过,并删除eventhub层的device对象。然后设置mNeedToScanDevices为true,因为mNeedToReopenDevices默认为false,所以不会执行这段代码;
//Reportanydevicesthathadlastbeenadded/removed.
while(mClosingDevices){
Device*device=mClosingDevices;
LOGV("Reportingdeviceclosed: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;
deletedevice;
mNeedToSendFinishedDeviceScan=true;
if(--capacity==0){
break;
}
}
检查mClosingDevices链表是否存在,如果存在,循环把需要删除的设备信息放在event里面,同时设置eventtype为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,就执行设备扫描。这个值 初始值 为true;在scanDevicesLocked里面,会调用scanDirLocked 打开/dev/input目录,并循环调用openDeviceLocked;在openDeviceLocked里面
status_t EventHub::openDeviceLocked(const char *devicePath) {
//打开一个input设备
intfd=open(devicePath,O_RDWR)
//Checktoseeifthedeviceisonourexcludedlist检查这个设备是否在我们的链表上,若是 之前有 打开这个 设备,那么就在;
for(size_ti=0;i<mExcludedDevices.size();i++){
constString8&item=mExcludedDevices.itemAt(i);
if(identifier.name==item){
LOGI("ignoringeventid%sdriver%s\n",devicePath,item.string());
close(fd);
return-1;
}
}
Device*device=newDevice(fd,deviceId,String8(devicePath),identifier);
//Loadtheconfigurationfileforthedevice.得到设备的idc配置文件,这就是为什么android4.0需要 idc文件
loadConfigurationLocked(device);
//Figureoutthekindsofeventsthedevicereports.得到设备各种配置
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,就设备的类型
//Seeifthisisatouchpad.
//Isthisanewmodernmulti-touchdriver?
if(test_bit(ABS_MT_POSITION_X,device->absBitmask)
&&test_bit(ABS_MT_POSITION_Y,device->absBitmask)){
//SomejoystickssuchasthePS3controllerreportaxesthatconflict
//withtheABS_MTrange.Trytoconfirmthatthedevicereallyis
//atouchscreen.
if(test_bit(BTN_TOUCH,device->keyBitmask)||!haveGamepadButtons){
device->classes|=INPUT_DEVICE_CLASS_TOUCH|INPUT_DEVICE_CLASS_TOUCH_MT;
}
//Isthisanoldstylesingle-touchdriver?
}elseif(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是多点触摸还是单点触摸,现在是不是看到和触摸屏有点关系了;
//Determinewhetherthedeviceisexternalorinternal.
if(isExternalDeviceLocked(device)){
device->classes|=INPUT_DEVICE_CLASS_EXTERNAL;
}
判断是不是外部设备,根据两个条件判断,一是在idc文件里面如果有“device.internal”存在,就是内部设备,否则是外部设备。如果没有这个域存在,根据硬件设备的总线判断,如果是usb和bluetoothbus,就是外部设备。这个留着后面有作用。
//将设备加入到mEpollFd监控里面
if(epoll_ctl(mEpollFd,EPOLL_CTL_ADD,fd,&eventItem))
//将设备加入需要打开设备链表里面
device->next=mOpeningDevices;
mOpeningDevices=device;
至此,/dev/input/下面所有的设备对于linux层都已经打开,并且都添加到了mEpollFd监控里面,但是android层面的device还没有添加和初始化,只是放在需要打开设备链表里面。接着设置mNeedToSendFinishedDeviceScan 为true。因为mNeedToScanDevices 初始化为true,因此第一次进入getEvents就会执行这部分代码。
while(mOpeningDevices!=NULL){
Device*device=mOpeningDevices;
LOGV("Reportingdeviceopened: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里面,同时设置eventtype为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/ 里面所有设备的变化;
//Grabthenextinputevent.
booldeviceChanged=false;
while(mPendingEventIndex<mPendingEventCount){
conststructepoll_event&eventItem=mPendingEventItems[mPendingEventIndex++];
//上面这段代码通过mPendingEventIndex和mPendingEventCount关系判断mEpollFd是否监听到了事件发生,如果有事件发生,从mPendingEventItems取出一个事件;
if(eventItem.data.u32==EPOLL_ID_INOTIFY){
if(eventItem.events&EPOLLIN){
mPendingINotify=true;
}else{
LOGW("Receivedunexpectedepollevent0x%08xforINotify.",eventItem.events);
}
continue;
}
//判断是否是EPOLL_ID_INOTIFY事件,即mINotifyFd有没有变化,也就是在有没有设备热拔插 发生,如果有设置mPendingINotify为true,继续循环取下一个事件。如果不是继续往下走。
if(eventItem.data.u32==EPOLL_ID_WAKE){
if(eventItem.events&EPOLLIN){
LOGV("awokenafterwake()");
awoken=true;
charbuffer[16];
ssize_tnRead;
do{
nRead=read(mWakeReadPipeFd,buffer,sizeof(buffer));
}while((nRead==-1&&errno==EINTR)||nRead==sizeof(buffer));
}else{
LOGW("Receivedunexpectedepollevent0x%08xforwakereadpipe.",
eventItem.events);
}
continue;
}
//判断是不是EPOLL_ID_WAKE事件,即mWakeReadPipeFd有没有变化,如果有设置awoken为true,继续循环取下一个事件。如果不是EPOLL_ID_WAKE事件,继续往下走,肯定是有设备 输入事件发生。
ssize_tdeviceIndex=mDevices.indexOfKey(eventItem.data.u32);
if(deviceIndex<0){
LOGW("Receivedunexpectedepollevent0x%08xforunknowndeviceid%d.",
eventItem.events,eventItem.data.u32);
continue;
}
//得到有事件发生的设备索引,如果deviceIndex<0,有错误发声,继续循环取下一个事件。
Device*device=mDevices.valueAt(deviceIndex);
if(eventItem.events&EPOLLIN){
int32_treadSize=read(device->fd,readBuffer,
sizeof(structinput_event)*capacity);
if(readSize==0||(readSize<0&&errno==ENODEV)){
//DevicewasremovedbeforeINotifynoticed.
LOGW("couldnotgetevent,removed?(fd:%dsize:%dbufferSize:%dcapacity:%derrno:%d)\n",
device->fd,readSize,bufferSize,capacity,errno);
deviceChanged=true;
closeDeviceLocked(device);
}elseif(readSize<0){
if(errno!=EAGAIN&&errno!=EINTR){
LOGW("couldnotgetevent(errno=%d)",errno);
}
}elseif((readSize%sizeof(structinput_event))!=0){
LOGE("couldnotgetevent(wrongsize:%d)",readSize);
}else{
int32_tdeviceId=device->id==mBuiltInKeyboardId?0:device->id;
size_tcount=size_t(readSize)/sizeof(structinput_event);
for(size_ti=0;i<count;i++){
conststructinput_event&iev=readBuffer[i];
LOGV("%sgot: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
//Usethetimespecifiedintheeventinsteadofthecurrenttime
//sothatdownstreamcodecangetmoreaccurateestimatesof
//eventdispatchlatencyfromthetimetheeventisenqueuedonto
//theevdevclientbuffer.
//
//Theevent'stimestampfortuitouslyusesthesamemonotonicclock
//timebaseastherestofAndroid.Thekerneleventdevicedriver
//(drivers/input/evdev.c)obtainstimestampsusingktime_get_ts().
//ThesystemTime(SYSTEM_TIME_MONOTONIC)functionweuseeverywhere
//callsclock_gettime(CLOCK_MONOTONIC)whichisimplementedasa
//systemcallthatalsoqueriesktime_get_ts().
event->when=nsecs_t(iev.time.tv_sec)*1000000000LL
+nsecs_t(iev.time.tv_usec)*1000LL;
LOGV("eventtime%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_terr=device->keyMap.keyLayoutMap->mapKey(iev.code,
&event->keyCode,&event->flags);
LOGV("iev.code=%dkeyCode=%dflags=0x%08xerr=%d\n",
iev.code,event->keyCode,event->flags,err);
}
event+=1;
}
capacity-=count;
if(capacity==0){
//Theresultbufferisfull.Resetthependingeventindex
//sowewilltrytoreadthedeviceagainonthenextiteration.
mPendingEventIndex-=1;
break;
}
}
}else{
LOGW("Receivedunexpectedepollevent0x%08xfordevice%s.",
eventItem.events,device->identifier.name.string());
}
}
根据设备索引的设备文件句柄,通过read函数读取input_event事件,读取个数为capacity,根据read返回值readSize除以sizeof(structinput_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。
//Reportaddedorremoveddevicesimmediately.
if(deviceChanged){
continue;
}
如果deviceChanged为true,结束本次循环,从头执行循环,即立即执行设备添加或删除。
//Returnnowifwehavecollectedanyeventsorifwewereexplicitlyawoken.
if(event!=buffer||awoken){
break;
}
如果event!=buffer,就表示有event事件发生,或者awoken存在,结束循环,立即返回给inputreader处理event事件。
mPendingEventIndex = 0;
调用epoll_wait(mEpollFd,mPendingEventItems,EPOLL_MAX_EVENTS,timeoutMillis)之后,读到的epoll_event事件保存在mPendingEventItems,总共的事件数保存在mPendingEventCount,即
mPendingEventCount=size_t(pollResult);
当然,在调用epoll_wait之前,mPendingEventIndex被清0,直正的事件处理在上面的代码中。epoll_event只表明某个设备上有事件,并不包含事件内容,具体事件内容需要通过read来读取。
//Alldone,returnthenumberofeventsweread.
returnevent-buffer;
返回得到的event个数,支持整个getEvents已经执行完成,所有的event事件都保存在inputreader传递的RawEvent里面,看看下面的图,理解数据结构的变化
2.7处理事件
在上面2.4节最后我们看到InputReaderThread线程里面会 循环调用 InputReader::loopOnce 也就是 循环调用
count=mEventHub->getEvents(timeoutMillis,mEventBuffer,EVENT_BUFFER_SIZE); 读取事件;上一节已经介绍了,得到事件后,接着调用
processEventsLocked(mEventBuffer,count)处理事件;
在processEventsLocked里面主要分两步处理:
1)处理来自于事件驱动设备的事件(processEventsForDeviceLocked)
2)处理设备增加、删除 和 修改事件 — 为处理事件做准备;
for(constRawEvent*rawEvent=rawEvents;count;){
int32_ttype=rawEvent->type;
size_tbatchSize=1;
//进入for循环,取得本次循环头一个rawEvent,然后判断事件type,如果小于FIRST_SYNTHETIC_EVENT,就表示是真正的input事件,如果大于等于,就表示是input设备变化事件。
if(type<EventHubInterface::FIRST_SYNTHETIC_EVENT){
int32_tdeviceId=rawEvent->deviceId;
while(batchSize<count){
if(rawEvent[batchSize].type>=EventHubInterface::FIRST_SYNTHETIC_EVENT
||rawEvent[batchSize].deviceId!=deviceId){
break;
}
batchSize+=1;
}
processEventsForDeviceLocked(deviceId,rawEvent,batchSize);
//判断下一事件type,从rawEvent数组里面取得属于同一个设备的连续input事件,然后交给设备处理程序去处理,如果后面的事件不属于同一个设备,或者事件type是FIRST_SYNTHETIC_EVENT以后的事件,就终止查询,运行processEventsForDeviceLocked。
}else{
switch(rawEvent->type){
caseEventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when,rawEvent->deviceId);
break;
caseEventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when,rawEvent->deviceId);
break;
caseEventHubInterface::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得到的事件都有一一对应的处理。
2.7.1处理事件准备 —-- 设备 添加删除
按照程序执行流程,应该是先有设备,然后才会有设备事件,所以先分析设备增加。其代码如下:
InputReader::addDeviceLocked
String8name=mEventHub->getDeviceName(deviceId);
uint32_tclasses=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("Deviceadded:id=%d,name='%s'(ignorednon-inputdevice)",deviceId,name.string());
}else{
LOGI("Deviceadded:id=%d,name='%s',sources=0x%08x",deviceId,name.string(),
device->getSources());
}
判断device的mapper是否存在,如果不存在,这个设备就不是inputdevice
mDevices.add(deviceId,device);
新建的InputDevice增加到InputReader::mDevices中
InputReader::createDeviceLocked
InputDevice*device=newInputDevice(&mContext,deviceId,name,classes);
创建一个inputreader层的device
//Externaldevices.
if(classes&INPUT_DEVICE_CLASS_EXTERNAL){
device->setExternal(true);
}
根据类型设置是否是外部设备
接下来就是根据类型给device创建和增加事件转换器,即mapper,我们只分析touch
//Touchscreensandtouchpaddevices.
if(classes&INPUT_DEVICE_CLASS_TOUCH_MT){
device->addMapper(newMultiTouchInputMapper(device));
}elseif(classes&INPUT_DEVICE_CLASS_TOUCH){
device->addMapper(newSingleTouchInputMapper(device));
}
根据多点还是单点分别创建事件转换器。我们只分析单点设备,我们的touch只有一个mapper---SingleTouchInputMapper
SingleTouchInputMapper::SingleTouchInputMapper
它继承自TouchInputMapper---InputMapper
做一些初始化的工作
InputDevice::addMapper
mMappers.add(mapper)
新建的InputMapper增加到InputDevice::mMappers
至此inputreader层的inputdevice创建完成,并且每个device都创建了一个对应的事件转换器。
创建完就要进行配置
device->configure(when,&mConfig,0);
InputDevice::configure
if(!isIgnored()){
if(!changes){//firsttimeonly
mContext->getEventHub()->getConfiguration(mId,&mConfiguration);
}
size_tnumMappers=mMappers.size();
for(size_ti=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){//firsttimeonly
//Configurebasicparameters.
configureParameters();
//Configurecommonaccumulators.
mCursorScrollAccumulator.configure(getDevice());
mTouchButtonAccumulator.configure(getDevice());
//Configureabsoluteaxisinformation.
configureRawPointerAxes();
//Prepareinputdevicecalibration.
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;
}elseif(gestureModeString=="spots"){
mParameters.gestureMode=Parameters::GESTURE_MODE_SPOTS;
}elseif(gestureModeString!="default"){
LOGW("Invalidvaluefortouch.gestureMode:'%s'",gestureModeString.string());
}
}
如果idc文件有touch.gestureMode存在,使用idc文件的配置
if(getEventHub()->hasInputProperty(getDeviceId(),INPUT_PROP_DIRECT)){
//Thedeviceisatouchscreen.
mParameters.deviceType=Parameters::DEVICE_TYPE_TOUCH_SCREEN;
}elseif(getEventHub()->hasInputProperty(getDeviceId(),INPUT_PROP_POINTER)){
//Thedeviceisapointingdevicelikeatrackpad.
mParameters.deviceType=Parameters::DEVICE_TYPE_POINTER;
}elseif(getEventHub()->hasRelativeAxis(getDeviceId(),REL_X)
||getEventHub()->hasRelativeAxis(getDeviceId(),REL_Y)){
//Thedeviceisacursordevicewithatouchpadattached.
//Bydefaultdon'tusethetouchpadtomovethepointer.
mParameters.deviceType=Parameters::DEVICE_TYPE_TOUCH_PAD;
}else{
//Thedeviceisatouchpadofunknownpurpose.
mParameters.deviceType=Parameters::DEVICE_TYPE_POINTER;
}
从驱动文件里面得到touch的类型
String8deviceTypeString;
if(getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)){
if(deviceTypeString=="touchScreen"){
mParameters.deviceType=Parameters::DEVICE_TYPE_TOUCH_SCREEN;
}elseif(deviceTypeString=="touchPad"){
mParameters.deviceType=Parameters::DEVICE_TYPE_TOUCH_PAD;
}elseif(deviceTypeString=="pointer"){
mParameters.deviceType=Parameters::DEVICE_TYPE_POINTER;
}elseif(deviceTypeString!="default"){
LOGW("Invalidvaluefortouch.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里面我们的触摸屏是usbbus,被配置成外部设备,触摸屏配置成DEVICE_TYPE_TOUCH_SCREEN,因此mParameters.associatedDisplayIsExternal等于1,及使用外部的显示配置。
至此TouchInputMapper::configureParameters配置完成
//Configurecommonaccumulators.
mCursorScrollAccumulator.configure(getDevice());
mTouchButtonAccumulator.configure(getDevice());
配置光标和按键加速,都是根据驱动文件或者idc文件,这个都不需要。
//Configureabsoluteaxisinformation.
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从驱动文件里面得到需要参数
//Prepareinputdevicecalibration.
parseCalibration();
resolveCalibration();
根据idc文件配置校正参数。
if(!changes||(changes&InputReaderConfiguration::CHANGE_POINTER_SPEED)){
//Updatepointerspeed.
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
}
如果是第一次配置或者是改变速度,需要updatepointerspeed
boolresetNeeded=false;
if(!changes||(changes&(InputReaderConfiguration::CHANGE_DISPLAY_INFO
|InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
|InputReaderConfiguration::CHANGE_SHOW_TOUCHES))){
//Configuredevicesources,surfacedimensions,orientationand
//scalingfactors.
configureSurface(when,&resetNeeded);
}
如果是第一次配置或者是显示等改变,需要调用configureSurface
TouchInputMapper::configureSurface
//Determinedevicemode.
if(mParameters.deviceType==Parameters::DEVICE_TYPE_POINTER
&&mConfig.pointerGesturesEnabled){
mSource=AINPUT_SOURCE_MOUSE;
mDeviceMode=DEVICE_MODE_POINTER;
}elseif(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
//EnsurewehavevalidXandYaxes.
if(!mRawPointerAxes.x.valid||!mRawPointerAxes.y.valid){
LOGW(INDENT"Touchdevice'%s'didnotreportsupportforXorYaxis!"
"Thedevicewillbeinoperable.",getDeviceName().string());
mDeviceMode=DEVICE_MODE_DISABLED;
return;
}
判断x和y参数是否有效,这里就是判断触摸屏x和y的坐标范围的,在eventhub里面,只要最大和最小不相等,就是有效的。如果无效,设备模式就是关闭的,不能使用。
//Getassociateddisplaydimensions.
if(mParameters.associatedDisplayId>=0){
if(!mConfig.getDisplayInfo(mParameters.associatedDisplayId,
mParameters.associatedDisplayIsExternal,
&mAssociatedDisplayWidth,&mAssociatedDisplayHeight,
&mAssociatedDisplayOrientation)){
LOGI(INDENT"Touchdevice'%s'couldnotquerythepropertiesofitsassociated"
"display%d.Thedevicewillbeinoperableuntilthedisplaysize"
"becomesavailable.",
getDeviceName().string(),mParameters.associatedDisplayId);
mDeviceMode=DEVICE_MODE_DISABLED;
return;
}
}
根据associatedDisplayId和associatedDisplayIsExternal得到显示屏的分辨率,associatedDisplayId在configureParameters里面设为0,associatedDisplayIsExternal根据触摸屏类型和bus设为1.
调用InputReaderConfiguration::getDisplayInfo得到设置好的surfacesize。
if(displayId==0){
constDisplayInfo&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;
}
returntrue;
}
}
可以看到android4.0里面,分内部和外部分辨率两种。如果info里面都是0,这个函数返回false,就表示android设备还没有走到设置surfacesize这一步,就会打印提示信息,稍后android启动里面就会执行设置surfacesize的程序。
//Configuredimensions.
int32_twidth,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类型设置宽和高参数,根据上面的分析宽和高就是取自显示屏的分辨率。
boolsizeChanged=mSurfaceWidth!=width||mSurfaceHeight!=height;
if(sizeChanged||deviceModeChanged){
LOGI("Devicereconfigured:id=%d,name='%s',surfacesizeisnow%dx%d,modeis%d",
getDeviceId(),getDeviceName().string(),width,height,mDeviceMode);
mSurfaceWidth=width;
mSurfaceHeight=height;
//ConfigureXandYfactors.
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){
//Sendreset,unlessthisisthefirsttimethedevicehasbeenconfigured,
//inwhichcasethereaderwillcallresetitselfafterallmappersareready.
getDevice()->notifyReset(when);
}
如果有改变而且需要reset,reader将reset自己
至此InputDevice的configure和mapperconfigure都已完成完成
配置完成就要进行初始化
device->reset(when);
调用InputDevice::reset
mapper->reset(when)
mapper初始化
至此整个InputReader::addDeviceLocked已经分析完成了,到了这一步,我们的整个input系统都已经准备好去接收真正的inputevent并处理。
分析设备删除,其代码如下:
voidInputReader::removeDeviceLocked(nsecs_twhen,int32_tdeviceId){
InputDevice*device=NULL;
ssize_tdeviceIndex=mDevices.indexOfKey(deviceId);
if(deviceIndex>=0){
device=mDevices.valueAt(deviceIndex);
mDevices.removeItemsAt(deviceIndex,1);
把设备从mDevices链表里面移除
}else{
LOGW("IgnoringspuriousdeviceremovedeventfordeviceId%d.",deviceId);
return;
}
if(device->isIgnored()){
LOGI("Deviceremoved:id=%d,name='%s'(ignorednon-inputdevice)",
device->getId(),device->getName().string());
}else{
LOGI("Deviceremoved:id=%d,name='%s',sources=0x%08x",
device->getId(),device->getName().string(),device->getSources());
}
device->reset(when);
清除device配置
deletedevice;
删除device
}
InputReader::processEventsLocked设备增加、删除处理总结:
它负责处理inputreader层Device增加、删除事件。增加事件的流程为:为一个新增的Device创建一个InputDevice,并增加到InputReader::mDevices中;根据新增加设备的class类别,创建对应的消息转换器(InputMapper),然后此消息转换器加入InputDevice::mMappers中。消息转换器负责把读取的RawEvent转换成特定的事件,以供应用程序使用。
EventHub与InputReader各自管理功能:
lEventHub管理一堆Device,每一个Device与Kernel中一个事件输入设备对应
lInputReader管理一堆InputDevice,每一个InputDevice与EventHub中的Device对应
lInputDevice管理一些与之相关的InputMapper,每个device类型不同,会有一个InputMapper或者多个InputMapper,如我们touch只有:SingleTouchInputMapper。
下面再来看看inputreader里面touch类的关系
2.7.2处理事件准备—设置surfacesize
上一节讲到inputdevice的touchmapper配置时,会得到surfacesize,如果得到为0,就会把mDeviceMode配置为DEVICE_MODE_DISABLED,表示这个设备暂时无法使用,因此即使我们的input系统都准备好了,但是touch还是无法使用。下面讲如何配置surfacesize。
在2.3节线程创建里面看到:
SystemServer.java(frameworks\base\services\java\com\android\server)里面
ServerThread::run调用
Slog.i(TAG,"WindowManager");
wm=WindowManagerService.main(context,power,
factoryTest!=SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot);
ServiceManager.addService(Context.WINDOW_SERVICE,wm);
下面接着就会调用
try{
wm.displayReady();wm就是上面创建的,为后面整个显示做准备
}catch(Throwablee){
reportWtf("makingdisplayready",e);
}
WindowManagerService.java(frameworks\base\services\java\com\android\server\wm)里面
displayReady里面调用
WindowManagerwm=(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里面后四个参数:
publicintgetRawWidth(){
intw=getRawWidthNative();
if(DEBUG_DISPLAY_SIZE)Slog.v(
TAG,"Returningrawdisplaywidth:"+w);
returnw;
}
privatenativeintgetRawWidthNative();
publicintgetRawHeight(){
inth=getRawHeightNative();
if(DEBUG_DISPLAY_SIZE)Slog.v(
TAG,"Returningrawdisplayheight:"+h);
returnh;
}
privatenativeintgetRawHeightNative();
这两个函数要调用native空间函数
android_view_Display.cpp(frameworks\base\core\jni)
staticjintandroid_view_Display_getRawWidthNative(
JNIEnv*env,jobjectclazz)
{
DisplayIDdpy=env->GetIntField(clazz,offsets.display);
returnSurfaceComposerClient::getDisplayWidth(dpy);
}
staticjintandroid_view_Display_getRawHeightNative(
JNIEnv*env,jobjectclazz)
{
DisplayIDdpy=env->GetIntField(clazz,offsets.display);
returnSurfaceComposerClient::getDisplayHeight(dpy);
}
可以看到通过surface的client端得到屏幕的分辨率。
如何得到外部分辨率
publicintgetRawExternalWidth(){
return1280;
}
/**
*IfthedisplayismirroredtoanexternalHDMIdisplay,returnsthe
*heightofthatdisplay.
*@hide
*/
publicintgetRawExternalHeight(){
return720;
}
可以看到如果设备外界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<PointerController>controller=mLocked.pointerController.promote();
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){
boolneedWake=!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进行处理。
至此,配置surfacesize执行部分结束了,但是size并没有真正配置到mExternalDisplay和mInternalDisplay里面,只是改变类型放在mConfigurationChangesToRefresh里面,真正的size还保存在NativeInputManager的mLocked里面
这时并没有输入事件或者设备变化发生,因此InputReader::loopOnce很快结束,进入下一次循环
InputReader::loopOnce接着调用
uint32_tchanges=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调用
{//acquirelock
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);
}//releaselock
outConfig就是inputreader里面的mConfig,因此调用
InputReaderConfiguration::setDisplayInfo
if(displayId==0){
DisplayInfo&info=external?mExternalDisplay:mInternalDisplay;
info.width=width;
info.height=height;
info.orientation=orientation;
}
看到没有,饶了一个大圈,这里才把surfacesize真正放在mExternalDisplay和mInternalDisplay里面,供后面调用InputReaderConfiguration::getDisplayInfo时使用。
refreshConfigurationLocked函数会在inputreader创建时执行一次,但那个时候inputdevice还没有创建,而且changes为0,因此不会执行下面的部分。
if(changes&InputReaderConfiguration::CHANGE_MUST_REOPEN){
mEventHub->requestReopenDevices();
如果改变类型是reopen,就调用eventhub的requestReopenDevices
EventHub::requestReopenDevices里面设置mNeedToReopenDevices=true,这个会在EventHub::getEvents里面进行判断,前面2.6节已经讲了
}else{
for(size_ti=0;i<mDevices.size();i++){
InputDevice*device=mDevices.valueAt(i);
device->configure(now,&mConfig,changes);
}
}
得到所有的device,循环调用每个device的configure去重新配置。
InputDevice::configure已经在InputReader::addDeviceLocked讲过了,刚开始第一次配置changes是0,表示需要全面的初始化。现在只需要配置改变的部分了。
至此,我们整个inputtouch的工作环境已经配置好了,就等有触摸事件发生然后处理了
2.7.3处理来自于事件驱动设备的事件
InputReader::processEventsForDeviceLocked,它负责处理来自于同一个设备且在mEventBuffer中连续的多个事件,其函数原型如下:
ssize_tdeviceIndex=mDevices.indexOfKey(deviceId);
if(deviceIndex<0){
LOGW("DiscardingeventforunknowndeviceId%d.",deviceId);
return;
}
得到发生事件设备索引
InputDevice*device=mDevices.valueAt(deviceIndex);
根据索引得到发生事件的device
if(device->isIgnored()){
//LOGD("DiscardingeventforignoreddeviceId%d.",deviceId);
return;
}
如果device没有mapper,就返回不做任何处理。
device->process(rawEvents,count);
调用process处理
InputDevice::process
for(constRawEvent*rawEvent=rawEvents;count--;rawEvent++)
一次取出每一个事件
for(size_ti=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);
input_report_abs(myInputDev,ABS_Y,event->y);
产生的事件:*type,code,value
EV_ABS,ABS_X,event->x
EV_ABS,ABS_Y,event->y
代码:
input_report_key(myInputDev,BTN_TOUCH,1);
产生的事件:*type,code,value
EV_KEY,BTN_TOUCH,1
代码:
input_sync(myInputDev);
它调用input_event(dev,EV_SYN,SYN_REPORT,0);
产生的事件:*type,code,value
EV_SYN,SYN_REPORT,0
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){
caseBTN_TOUCH:
mBtnTouch=rawEvent->value;
break;
可以看到把BTN_TOUCH的值放在mBtnTouch里面
接着处理坐标信息:
mSingleTouchMotionAccumulator.process(rawEvent)
SingleTouchMotionAccumulator::process
caseABS_X:
mAbsX=rawEvent->value;
break;
caseABS_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继续处理
//Resetstatethatwewillcomputebelow.
mCurrentFingerIdBits.clear();
mCurrentStylusIdBits.clear();
mCurrentMouseIdBits.clear();
mCurrentCookedPointerData.clear();
这几个清零,后面填入相应的值
if(mDeviceMode==DEVICE_MODE_DISABLED){
//Dropallinputifthedeviceisdisabled.
mCurrentRawPointerData.clear();
mCurrentButtonState=0;
}
如果设备状态是关闭的,就把mCurrentRawPointerData保存的数据清除,返回
uint32_tpolicyFlags=0;
boolinitialDown=mLastRawPointerData.pointerCount==0
&&mCurrentRawPointerData.pointerCount!=0;
boolbuttonsPressed=mCurrentButtonState&~mLastButtonState;
if(initialDown||buttonsPressed){
//Ifthisisatouchscreen,hidethepointeronaninitialdown.
if(mDeviceMode==DEVICE_MODE_DIRECT){
getContext()->fadePointer();
}
//Initialdownsonexternaltouchdevicesshouldwakethedevice.
//Wedon'tdothisforinternaltouchscreenstopreventthemfromwaking
//upinyourpocket.
//TODO:Usetheinputdeviceconfigurationtocontrolthisbehaviormorefinely.
if(getDevice()->isExternal()){
policyFlags|=POLICY_FLAG_WAKE_DROPPED;
}
}
判断是不是第一次按下,如果是,Ifthisisatouchscreen,hidethepointeronaninitialdown。
如果是外部设备,就唤醒整个系统,如果是内部设备,就不用唤醒,注释写的很清楚,有可能放在口袋里面误触摸唤醒系统
//Consumerawoff-screentouchesbeforecookingpointerdata.
//Iftouchesareconsumed,subsequentcodewillnotreceiveanypointerdata.
if(consumeRawTouches(when,policyFlags)){
mCurrentRawPointerData.clear();
如果是唤醒设备的点击,就把mCurrentRawPointerData清零,只需要唤醒设备就行了。
cookPointerData();
进行触摸坐标到原始坐标的转换
TouchInputMapper::cookPointerData
首先进行一系列的坐标校准,接下来进行坐标转换
//XandY
//Adjustcoordsforsurfaceorientation.
floatx,y;
switch(mSurfaceOrientation){
caseDISPLAY_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;
caseDISPLAY_ORIENTATION_180:
x=float(mRawPointerAxes.x.maxValue-in.x)*mXScale;
y=float(mRawPointerAxes.y.maxValue-in.y)*mYScale;
break;
caseDISPLAY_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
boolorientationChanged=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这个里面说的:
Foratouchscreen,thesystemautomaticallyinterpolatesthereportedtouchpositionsinsurfaceunitstoobtaintouchpositionsindisplaypixelsaccordingtothefollowingcalculation:
displayX=(x-minX)*displayWidth/(maxX-minX+1)
displayY=(y-minY)*displayHeight/(maxY-minY+1)
接着把转换后的坐标放在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实例。
补充2)在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMapper的mContext中。
dispatchHoverEnterAndMove(when,policyFlags);
调用dispatchMotion
至此整个processEventsLocked处理流程结束,已经把来自于事件设备的事件处理之后放入到各种NotifyArgs(如NotifyMotionArgs)之中,然后把这些各种NotifyArgs加入InputReader::mQueuedListener::mArgsQueue链表中。接着InputReader::loopOnce调用
mQueuedListener->flush()
Flush函数就是要把mArgsQueue中的所有NotifyArgs进行处理。
voidQueuedInputListener::flush(){
size_tcount=mArgsQueue.size();
for(size_ti=0;i<count;i++){
NotifyArgs*args=mArgsQueue[i];
args->notify(mInnerListener);
deleteargs;
}
mArgsQueue.clear();
}
调用链表中每个NotifyArgs的notify函数,且有一个有意思的参数mInnerListener,这个参数在前面多次提到过,它是在创建mQueuedListener时提供的,它其实就是InputManager中的mDispatcher,前面一直在InputReader中打转转,现在终于看到InputDispatcher登场了,说明事件很快就可以谢幕了。
再向下看一下吧,这么多类NotifyArgs,为描述方便,下面以NotifyMotionArgs为例,其代码为:
voidNotifyMotionArgs::notify(constsp<InputListenerInterface>&listener)const{
listener->notifyMotion(this);
}
下面就看看InputDispatcher(mDispatcher)的notifyMotion函数做了些什么。这个InputDispatcher::notifyMotion(constNotifyMotionArgs*args)可就不简单了。
在InputDispatcher::notifyMotion中,
1)根据NotifyMotionArgs提供的信息,构造一个MotionEvent,再调用mPolicy->filterInputEvent看是否需要丢弃此事件,如果需要丢弃则马上返加。其中mPolicy为NativeInputManager实例,在构造InputDispatcher时提供的参数。
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里面
事件处理相关数据结构如下图所示:
至此的消息结构变化流程:
2.8分发事件
前面线程启动提到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<MotionEntry的基类>,
2)调用InputDispatcher::dispatchMotionLocked处理此MotionEntry
3)调用InputDispatcher::dispatchEventToCurrentInputTargetsLocked
对于InputDispatcher::mCurrentInputTargets中的每一个InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked,
InputDispatcher::dispatchEventToCurrentInputTargetsLocked
for(size_ti=0;i<mCurrentInputTargets.size();i++){
constInputTarget&inputTarget=mCurrentInputTargets.itemAt(i);
ssize_tconnectionIndex=getConnectionIndexLocked(inputTarget.inputChannel);
if(connectionIndex>=0){
sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime,connection,eventEntry,&inputTarget,
resumeWithAppendedMotionSample);
}else{
#ifDEBUG_FOCUS
LOGD("Droppingeventdeliverytotargetwithchannel'%s'becauseit"
"isnolongerregisteredwiththeinputdispatcher.",
inputTarget.inputChannel->getName().string());
#endif
}
}
InputDispatcher::prepareDispatchCycleLocked
1)调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。
2)调用activateConnectionLocked把当前Connection增加到InputDispatcher::mActiveConnections链表中
3)调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmembuffer中,调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来消费吧!
3内核层驱动
请参考网上的linux内核input子系统解析
更多相关文章
- 详解 Android(安卓)的 Activity 组件
- (三)Android事件分发机制 - Activity篇
- [置顶] android调用第三方库——第四篇——调用多个第三方库
- Android的Camera架构介绍
- Android与js交互实例
- Android调用WCF
- @+id/和android:id的区别
- Android与js交互实例
- Android(安卓)上Camera分析