Input子系统架构包括内核层与框架层详解.doc_第1页
Input子系统架构包括内核层与框架层详解.doc_第2页
Input子系统架构包括内核层与框架层详解.doc_第3页
Input子系统架构包括内核层与框架层详解.doc_第4页
Input子系统架构包括内核层与框架层详解.doc_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第1章 android input子系统架构1.1 input服务的启动在android的开机过程中,系统中的服务很多都是由systemserver中启动的。systemserver的代码中有这么一句话。framework/base/services/java/com/android/server/systemserver.javaslog.i(tag, window manager); wm = windowmanagerservice.main(context, power, factorytest != systemserver.factory_test_low_level,!firstboot);在这里new了一个windowmanagerservice的类,我们找到这个类的构造函数。framework/base/services/java/com/android/server/wm/windowmanagerserver.javaprivate windowmanagerservice(context context, powermanagerservice pm, boolean haveinputmethods, boolean showbootmsgs) minputmanager = new inputmanager(context, this);minputmanager.start();在windowmanagerservice的构造函数中又new了一个inputmanager类。inputmanager类是整个android的input的上层代码最重要的类,就是通过这个类繁衍出了整个复杂的android的input子系统。作用就好像zygote的孕育着android的各个服务,而inputmanager就是负责将整个android的input子系统。framework/base/services/java/com/android/server/wm/windowmanagerserver.javapublic inputmanager(context context, windowmanagerservice windowmanagerservice) looper looper = windowmanagerservice.mh.getlooper();nativeinit(mcontext, mcallbacks, looper.getqueue();在inputmanger的构造函数中,调用了nativeinit这个方式,看到native开头或者结尾的函数,一般都是jni。在inputmanager的jni可以找到这个函数的实现。framework/base/services/jni/com_android_server_inputmanager.javastatic jninativemethod ginputmanagermethods = nativeinit, (landroid/content/context;lcom/android/server/wm/inputmanager$callbacks;landroid/os/messagequeue;)v, (void*) android_server_inputmanager_nativeinit ,简单介绍下jni的代码风格,第一个引号括起来的函数就是我们java代码的函数原型,中间的引号中的就是代表java原型函数的参数。而最后的那个函数就是在对应的函数。一般都是c+代码。framework/base/services/jni/com_android_server_inputmanager.javastatic void android_server_inputmanager_nativeinit(jnienv* env, jclass clazz, jobject contextobj, jobject callbacksobj, jobject messagequeueobj) gnativeinputmanager = new nativeinputmanager(contextobj, callbacksobj, looper);在jni的代码中,又构造了一个重要的nativeinputmanager类,这是个c+的本地类。已经不在是之前了那个java的inputmanager类。接下来看看nativeinputmanager的构造函数。framework/base/services/jni/com_android_server_inputmanager.javanativeinputmanager:nativeinputmanager(jobject contextobj, jobject callbacksobj, const sp& looper) :mlooper(looper) sp eventhub = new eventhub(); minputmanager = new inputmanager(eventhub, this, this);这里new了两个类,eventhub和inputmanager类。eventhub就是input子系统的hal层了,负责将linux的所有的input设备打开并负责轮询读取他们的上报的数据,后面会详细介绍,这里先简单介绍一下。inputmanager类主要是负责管理input event,有inputreader从eventhub读取事件,然后交给inputdispatcher进行分发。framework/base/services/input/inputmanager.cppinputmanager:inputmanager( const sp& reader, const sp& dispatcher) : mreader(reader), mdispatcher(dispatcher) initialize();void inputmanager:initialize() mreaderthread = new inputreaderthread(mreader); mdispatcherthread = new inputdispatcherthread(mdispatcher);在inputmanager中的initialize的初始化了两个线程。一个是inputreaderthread,负责从eventhub中读取事件,另外一个是inputdispatcherthread线程,主要负责分发读取的事件去处理。framework/base/services/java/com/android/server/wm/windowmanagerserver.javaprivate windowmanagerservice(context context, powermanagerservice pm, boolean haveinputmethods, boolean showbootmsgs) minputmanager.start();在开始的时候,new了一个inputmanager,然后在继续调用其start方法。framework/base/services/java/com/android/server/wm/windowmanagerserver.javapublic void start() slog.i(tag, starting input manager); nativestart(); registerpointerspeedsettingobserver(); registershowtouchessettingobserver(); updatepointerspeedfromsettings(); updateshowtouchesfromsettings(); nativestart()跑到jni的代码中去了,跟上面的方式一样。framework/base/services/jni/com_android_server_inputmanager.javastatic jninativemethod ginputmanagermethods = nativestart, ()v, (void*) android_server_inputmanager_nativestart ,static void android_server_inputmanager_nativestart(jnienv* env, jclass clazz) status_t result = gnativeinputmanager-getinputmanager()-start();在java代码中用了nativestart(),然后jni中又调用了nativeinputmanager的start方法。在native的inputmanager中找到start的实现。framework/base/services/input/inputmanager.cppstatus_t inputmanager:start() status_t result = mdispatcherthread-run(inputdispatcher, priority_urgent_display);result = mreaderthread-run(inputreader, priority_urgent_display);这个方法就是在前面inputmanager中的构造函数initialize中的两个线程运行起来。先看inputdispatcher线程运行的情况,然后就是inputreader线程。framework/base/services/input/inputdispatcher.cppbool inputdispatcherthread:threadloop() mdispatcher-dispatchonce(); return true;inputdispatcher线程调用了dispatcher的dispatchonce的方法。同样的inputreader线程也会调用reader的readeronce的方法。framework/base/services/input/inputdispatcher.cppvoid inputdispatcher:dispatchonce() dispatchonceinnerlocked(&nextwakeuptime); int timeoutmillis = tomillisecondtimeoutdelay(currenttime, nextwakeuptime); mlooper-pollonce(timeoutmillis);dispatchonceinnerlocked是处理input输入消息,mlooper-pollonce(timeoutmillis)是等待下次输入消息的事件。先看下消息在dispatchonceinnerlocked函数中是如何处理的。framework/base/services/input/inputdispatcher.cppvoid inputdispatcher:dispatchonceinnerlocked(nsecs_t* nextwakeuptime) case evententry:type_keydone = dispatchkeylocked(currenttime, typedentry, &dropreason, nextwakeuptime);case evententry:type_motion: done = dispatchmotionlocked(currenttime, typedentry, &dropreason, nextwakeuptime);这个函数比较长,input事件在android的上层通过两个队列来保存,分别是inboundqueue和outboundqueue。当有input事件产生时候,会判断inboundqueue是否为空,如果事件不为空的话,就从队列中取出这个input事件,然后根据input事件的类型来分发事件给不同的处理函数,比较常见的是key和motion事件。不管是key事件也好还是motion事件都会调用dispatcheventtocurrentinputtargetslocked(currenttime, entry, false);这个函数来继续处理。framework/base/services/input/inputdispatcher.cppvoid inputdispatcher:dispatcheventtocurrentinputtargetslocked(nsecs_t currenttime, evententry* evententry, bool resumewithappendedmotionsample) preparedispatchcyclelocked(currenttime, connection, evententry, & inputtarget, resumewithappendedmotionsample);在这个函数中会继续调用preparedispatchcyclelocked方法来继续处理。而在preparedispatchcyclelocked中又会继续调用startdispatchcyclelocked(currenttime, connection)来进一步处理。framework/base/services/input/inputdispatcher.cppvoid inputdispatcher:startdispatchcyclelocked(nsecs_t currenttime, const sp& connection) switch (evententry-type) case evententry:type_key: status = connection-inputpublisher.publishkeyeventcase evententry:type_motion: status = connection-inputpublisher.publishmotioneventstatus = connection-inputpublisher.senddispatchsignal();这个函数主要是根据input事件的类型来分发给不同的函数去处理,如果是key类型的事件就调用inputpublisher类的publishkeyevent,如果是motion类的事件就会调用inputpublisher类的publishmotionevent方法。并在最后发一个senddispatchsignal。framework/base/libs/ui/inputtransport.cppstatus_t inputpublisher:publishinputevent(int ashmemfd = mchannel-getashmemfd();int result = ashmem_pin_region(ashmemfd, 0, 0);msemaphoreinitialized = true; msharedmessage-consumed = false; msharedmessage-type = type; msharedmessage-deviceid = deviceid;msharedmessage-source = source;利用publisher中的publishinputevent将input event写入共享内存。这边产生了事件,另外一边必然会有个地方回去消费这个事件。注意到上面的代码中,最后发送了一个senddispatchsignal。framework/base/libs/ui/inputtransport.cppstatus_t inputpublisher:senddispatchsignal() return mchannel-sendsignal(input_signal_dispatch);这个函数直接调用了inputchannel的sendsignal方法。继续找到inputchannel的sendsignal实现。framework/base/libs/ui/inputtransport.cppstatus_t inputchannel:sendsignal(char signal) do nwrite = :write(msendpipefd, & signal, 1); while (nwrite = -1 & errno = eintr);而在注册inputchannel的时候就曾经注册了当looper接收到了信号的一个回调函数。framework/base/services/input/inputdispatcher.cppstatus_t inputdispatcher:registerinputchannel(const sp& inputchannel, const sp& inputwindowhandle, bool monitor) mlooper-addfd(receivefd, 0, alooper_event_input, handlereceivecallback, this);在handlereceivecallback中,作为回调函数然后调用inputconsumer的consume函数来消费从inputreader中读取过来的inputevent。framework/base/core/jni/android_view_inputqueue.cppint nativeinputqueue:handlereceivecallback(int receivefd, int events, void* data) status = connection-inputconsumer.consume(& connection-inputeventfactory, & inputevent);回过头来看之前的inputreader线程,在inputmanager的start方法被调用了,input的线程也就开始运行了。framework/base/services/input/inputreader.cppbool inputreaderthread:threadloop() mreader-looponce(); return true;在inputreader的looponce中会调用eventhub的getevents方法。这个方法会和linux内核的input子系统打交道。framework/base/services/input/inputreader.cppvoid inputreader:looponce() size_t count = meventhub-getevents(timeoutmillis, meventbuffer, event_buffer_size);if (count) processeventslocked(meventbuffer, count); 这个函数主要通过eventhub的getevents来获取input事件。framework/base/services/input/eventhub.cppsize_t eventhub:getevents(int timeoutmillis, rawevent* buffer, size_t buffersize) struct input_event readbufferbuffersize;for (;) if (mneedtoscandevices) mneedtoscandevices = false; scandeviceslocked(); mneedtosendfinisheddevicescan = true; 在eventhub初始化的时候mneedtoscandevices的值是ture的,所以会直接进入到scandeviceslocked。而在内核里面所有的input device在注册的时候都会在linux的文件系统下的/dev/input 下面,所以按照一般的hal的思想,如果要去操作这个设备,首先还是要打开这个设备节点的。framework/base/services/input/eventhub.cppvoid eventhub:scandeviceslocked() status_t res = scandirlocked(device_path); if(res 0) loge(scan dir failed for %sn, device_path); status_t eventhub:scandirlocked(const char *dirname)opendevicelocked(devname);代码中的while循环会对device_path(/dev/input)下的所有的设备节点调用opendevicelocked方法。framework/base/services/input/eventhub.cppstatus_t eventhub:opendevicelocked(const char *devicepath) int fd = open(devicepath, o_rdwr);inputdeviceidentifier identifier;if(ioctl(fd, eviocgname(sizeof(buffer) - 1), &buffer) next = mopeningdevices; mopeningdevices = device;return 0;首先通过open系统调用得到设备节点的文件描述符,然后新构造一个叫inputdeviceidentifier类。接着通过对刚才得到的设备节点描述下ioctl的命令获取设备的一些简单信息,譬如:设备的名字,设备驱动的版本号,设备的唯一id,和描述符轮询的方式。得到的这些信息保存在inputdeviceidentifier类里面。最后又构造了一个device类,其中设备描述符和刚才的构造inputdeviceidentifier类作为参数重新构造了device类。然后在构造成功了device类又会通过ioctl系统调用获取input设备的一些比较重要的参数。比如:设备上报事件的类型是相对事件还是绝对事件,相对事件一般是指像鼠标滑动,绝对事件就好比触摸屏上报的坐标,设备所属的class等一些比较重要的信息。举一些例子:input_device_class_keyboard(按键类型),input_device_class_cursor(带游标类型:鼠标和轨迹球等),input_device_class_touch(触摸类型:单点触摸或多点触摸),input_device_class_touch_mt(这个类型特指多点触摸)等。如果一个设备的驱动没有指明设备的类型的话,那么他在android中上报的数据时不会被处理的。这个函数的最后是将input设备的文件描述符加入到轮询的集合中去,如果接收到事件就会去处理。framework/base/services/input/inputreader.cppvoid inputreader:looponce() size_t count = meventhub-getevents(timeoutmillis, meventbuffer, event_buffer_size);if (count) processeventslocked(meventbuffer, count); 回到之前的inputreader的线程中,通过eventhub的getevents方法得到了input事件。函数返回的是获取的事件数目。如果事件不为零或者负数就会调用processeventlocked来处理。framework/base/services/input/inputreader.cppvoid inputreader:processeventslocked(const rawevent* rawevents, size_t count) processeventsfordevicelocked(deviceid, rawevent, batchsize);case eventhubinterface:device_added: adddevicelocked(rawevent-when, rawevent-deviceid);case eventhubinterface:device_removed: removedevicelocked(rawevent-when, rawevent-deviceid);case eventhubinterface:finished_device_scan: handleconfigurationchangedlocked(rawevent-when);在处理input的事件时候,如果不是设备的添加,删除和完成扫描的时候。就会调用processeventsfordevicelocked来处理。framework/base/services/input/inputreader.cppvoid inputreader:processeventsfordevicelocked(int32_t deviceid, const rawevent* rawevents, size_t count) device-process(rawevents, count);这个函数也很简单直接回调了device的process方法来进行处理,这个device就是在之前的eventhub打开设备时候构造了一个device类。下面来具体看看device的process是如何进行处理的。framework/base/services/input/inputreader.cppvoid inputdevice:process(const rawevent* rawevents, size_t count) for (size_t i = 0; i process(rawevent); 这里直接调用了mapper的process。那inputmapper是什么时候初始化的呢?前面提到如果设备不是设备的添加或删除的时候就调用processeventsfordevicelocked来处理。也就是说当设备第一次添加的时候,就会调用adddevicelocked。inputmapper就是从这个地方注册过来的。具体看下面的代码:framework/base/services/input/inputreader.cppvoid inputreader:adddevicelocked(nsecs_t when, int32_t deviceid) inputdevice* device = createdevicelocked(deviceid, name, classes);inputdevice* inputreader:createdevicelocked(int32_t deviceid,const string8& name, uint32_t classes) inputdevice* device = new inputdevice(&mcontext, deviceid, name, classes);if (classes & input_device_class_switch) device-addmapper(new switchinputmapper(device);if (classes & input_device_class_cursor) device-addmapper(new cursorinputmapper(device);if (classes & input_device_class_touch_mt) device-addmapper(new multitouchinputmapper(device); else if (classes & input_device_class_touch) device-addmapper(new singletouchinputmapper(device);if (classes & input_device_class_joystick) device-addmapper(new joystickinputmapper(device);return device;从上面的代码可以非常明显的看出,inputmapper是根据inputdevice的class来构造的这么一个类。也就是说如果我们的设备是switch类就为这个设备构造一个switchinputmapper类,我们假设我们现在的事件是由触摸屏上报的事件处理流程。在前面处理的过程中我们就会调用multitouchinputmapper的process来继续处理。也就是说当处理到mapper-process的时候,代码就会根据具体的设备class类型来处理相应的事件。这么多inputmapper,我们就以multitouchinputmapper的流程继续来分析事件的处理流程。framework/base/services/input/inputreader.cppvoid multitouchinputmapper:process(const rawevent* rawevent) touchinputmapper:process(rawevent); mmultitouchmotionacess(rawevent);void multitouchmotionaccumulator:process(const rawevent* rawevent) switch (rawevent-scancode) case abs_mt_position_x: slot-minuse = true; slot-mabsmtpositionx = rawevent-value;case abs_mt_position_y: slot-minuse = true; slot-mabsmtpositiony = rawevent-value;case abs_mt_touch_major: slot-minuse = true; slot-mabsmttouchmajor = rawevent-value; break;case abs_mt_touch_minor: slot-minuse = true; slot-mabsmttouchminor = rawevent-value; slot-mhaveabsmttouchminor = true;在这个处理过程中将上报的原始事件的一些重要参数都赋值给了一个叫slot的结构的成员。我们知道多点触摸上报的一些参数主要包括:x轴和y轴的坐标点,abs_mt_touch_major代表手指和触摸屏接触面的长轴(如果假定人手指和触摸屏接触面一般都是椭圆),abs_mt_touch_minor代表手指和屏幕接触面的短轴等等一些重要的参数都对号入座的赋值给了一个叫slot的结构体成员,具体更多的参数代表什么意思我就不多说了(大家可以参照linux内核文档,多点触摸协议)。multitouchinputmapper的process处理分了两部分,首先是调用了touchinputmapper的process方法,然后调用了mmultitouchmotionacess来进行处理。先看touchinputmapper的处理流程。framework/base/services/input/inputreader.cppvoid touchinputmapper:process(const rawevent* rawevent) if (rawevent-type = ev_syn & rawevent-scancode = syn_report) sync(rawevent-when); 在linux内核往上层上报input event的时候是由顺序的,以触摸屏为例,input_report_abs会被用来上报一些绝对事件(如:接触面积半径,x和y轴的坐标点),但是在每一次上报完成后都会调用input_sync(input_dev);来表示一次完整的事件上报,换句话说这个rawevent-type为ev_syn的事件就是许多input event的分隔符。framework/base/services/input/inputreader.cppvoid touchinputmapper:sync(nsecs_t when) bool havepointerids = true; mcurrentrawpointerdata.clear();synctouch(when, &havepointerids);cookpointerdata();dispatchhoverexit(when, policyflags);dispatchtouches(when, policyflags);dispatchhoverenterandmove(when, policyflags);通过同步触摸事件,然后调用cookpointerdata来计算x和y的坐标,前后两次上报数据的距离,和根据缩放的大小及方向来判断是不是需要旋转屏幕。然后dispatchtouches中又调用了disaptchmotion方法。framework/base/services/input/inputreader.cppvoid touchinputmapper:dispatchmotion(nsecs_t when, uint32_t policyflags, uint32_t source,) getlistener()-notifymotion(&args);通过getlister的方法得到inputlistener并调用其notifymotion。void queuedinputlistener:notifymotion(const notifymotionargs* args) margsqueue.push(new notifymotionargs(*args);在构造了一个notifymotionargs类之后,我们回到之前的inputreader的looponce方法中,在最后调用了mqueuedlistener-flush();flush函数就是要把margsqueue中的所有notifyargs进行处理。为描述方便,先看看其代码:framework/base/services/input/inputlisenter.cppvoid queuedinputlistener:flush() size_t count = margsqueue.size(); for (size_t i = 0; i notify(minnerlistener); delete args; margsqueue.clear();继续在notifyargs中调用了其notify的方法。还是在这个文件中可以看看我们notifymotionargs的notify方法。framework/base/services/input/inputlisenter.cppvoid notifymotionargs:notify(const sp& listener) const listener-notifymotion(this);在inputdispatcher.h中inputdispatcherinterface是继承inputlistenerinterface,父类调用了子类的虚函数notifymotion的实例化。在inputdispacher的notifymotion中继续,这就重新回到了我们上面分析的inputdispatch线程的代码。正好完成了一边生产事件而在另一边消费事件。

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论