分析一下Android是如何读取按键及TouchPanel的驱动的。主要在

$(ANDROID_DIR)/frameworks/base/libs/ui/EventHub.cpp 这个文件中,这是在HAL层,将一步步分析Android上层是如何接受事件的。 一,先看一下AndroidHAL ClassEventHub在$(ANDROID_DIR)/frameworks/base/include/ui/eventhub.h定义. i.scan_dir(constchar*dirname)//dirname="/dev/input" 扫描dirname目录,该目录下有event0,event1...,等设备. ii.open_device(devname); 打开/dev/input/event0,/dev/input/event1等设备. 这里以打开/dev/input/event0设备为例,分析按键的底层处理. for(attempt=0;attempt<10;attempt++){
fd=open(deviceName,O_RDWR);
if(fd>=0)break;
usleep(100);
}
首先会打开传进来的设备.然后会获取version,id等信息. if(ioctl(fd,EVIOCGNAME(sizeof(name)-1),&name)<1){
//fprintf(stderr,"couldnotgetdevicenamefor%s,%s/n",deviceName,strerror(errno));
name[0]='/0'
}
获取drivername,在这里也就是/dev/input/evevnt0,也就是要到Driver里面去读取.
这个名字很重要,之后要与keyboardmap相匹配. 这里返回的值是:name="wayland_m_ebook_key_input" 为什么会返回这个值?请看event0的linuxdriver. wayland_m_ebook_keypad_probe()函数中,有以下语句: gpio_key_input->name="wayland_m_ebook_key_input". 所以这个值是在这个时候设置的。 intdevid=0;
while(devid<mNumDevicesById){
if(mDevicesById[devid].device==NULL){
break;
}
devid++;
}
if(devid>=mNumDevicesById){
device_ent*new_devids=(device_ent*)realloc(mDevicesById,
sizeof(mDevicesById[0])*(devid+1));
if(new_devids==NULL){
LOGE("outofmemory");
return-1;
}
mDevicesById=new_devids;
mNumDevicesById=devid+1;
mDevicesById[devid].device=NULL;
mDevicesById[devid].seq=0;
}
分配newdevice,将device信息保存至mDeviceById[]数组中. mNumDevicesById:device的数量 mDevicesById:devive的信息 new_mFDs=(pollfd*)realloc(mFDs,sizeof(mFDs[0])*(mFDCount+1));
new_devices=(device_t**)realloc(mDevices,sizeof(mDevices[0])*(mFDCount+1));
为new_mFDs,mFDs分配空间,以备之后保存每个event(x)的fd.
mFDs[mFDCount].fd=fd;
mFDs[mFDCount].events=POLLIN;
将fd放到mFDs数组中.
//Seeifthisisakeyboard,andclassifyit.
uint8_tkey_bitmask[(KEY_MAX+1)/8];
memset(key_bitmask,0,sizeof(key_bitmask));
LOGV("Gettingkeys...");
if(ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(key_bitmask)),key_bitmask)>=0){
//LOGI("MAP/n");
//for(inti=0;i<((KEY_MAX+1)/8);i++){
//LOGI("%d:0x%02x/n",i,key_bitmask[i]);
//}
for(inti=0;i<((BTN_MISC+7)/8);i++){
if(key_bitmask[i]!=0){
device->classes|=CLASS_KEYBOARD;
break;
}
}
if((device->classes&CLASS_KEYBOARD)!=0){
device->keyBitmask=newuint8_t[sizeof(key_bitmask)];
if(device->keyBitmask!=NULL){
memcpy(device->keyBitmask,key_bitmask,sizeof(key_bitmask));
}else{
deletedevice;
LOGE("outofmemoryallocatingkeybitmask");
return-1;
}
}
}
如果是keyboard的设备.
if(test_bit(BTN_TOUCH,key_bitmask)){
uint8_tabs_bitmask[(ABS_MAX+1)/8];
memset(abs_bitmask,0,sizeof(abs_bitmask));
LOGV("Gettingabsolutecontrollers...");
if(ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(abs_bitmask)),abs_bitmask)>=0)
{
if(test_bit(ABS_X,abs_bitmask)&&test_bit(ABS_Y,abs_bitmask)){
device->classes|=CLASS_TOUCHSCREEN;
}
}
} 继续分析Android是如何进行keyboard映射的: chartmpfn[sizeof(name)];
charkeylayoutFilename[300];
//amoredescriptivename
device->name=name;
//replaceallthespaceswithunderscores
strcpy(tmpfn,name);
for(char*p=strchr(tmpfn,'');p&&*p;p=strchr(tmpfn,''))
*p='_'
//findthe.klfileweneedforthisdevice
constchar*root=getenv("ANDROID_ROOT");
snprintf(keylayoutFilename,sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl",root,tmpfn);
booldefaultKeymap=false;
if(access(keylayoutFilename,R_OK)){
snprintf(keylayoutFilename,sizeof(keylayoutFilename),
"%s/usr/keylayout/%s",root,"qwerty.kl");
defaultKeymap=true;
}
ANDROID_ROOT一般都设置为/system tmpfn也就是name中的内容。而name=wayland_m_ebook_key_input 所以keylayoutFilename=/system/usr/keylayout/wayland_m_ebook_key_input.kl. 这个文件保存有按键的信息。可以在这个文件中修改按键的键值。 如果没有这个文件则会去读define的qwerty.kl. device->layoutMap->load(keylayoutFilename); load/system/usr/keylayout/wayland_m_ebook_key_input.kl这个文件进行分析。 KeyLayoutMap::load在KeyLayoutMap.cpp中实现。 把按键的映射关系保存在:KeyedVector<int32_t,Key>m_keys;中。 iii.EventHub::getEvent() 主要通过read()函数读取按键事件及进行Map键值映射。
二、再来看看jni层 com_android_server_KeyInputQueue.cpp 看看其中几行的代码: staticJNINativeMethodgInputMethods[]={
/*name,signature,funcPtr*/
{"readEvent","(Landroid/view/RawInputEvent;)Z",
(void*)android_server_KeyInputQueue_readEvent},
可以看出,上层(即framework层)调用的接口是readEvent,实现函数是本文件的android_server_KeyInputQueue_readEvent(). 这个函数调用了getEvent读取事件。也就是EventHub.cpp中的EventHub::getEvent(). readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。 三、再看看jni层对应的java层(经常封装成.jar档案) KeyInputQueue.java 在 frameworks/base/services/java/com/android/server/KeyInputQueue.java里创建了 一个InputDeviceReader线程,它循环的读取事件,然后把事件放入事件队列里,包括key/touchpanel.
四、事件分发

输入事件分发线程在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。


按键触摸屏流程分析:

WindowManagerService类的构造函数

WindowManagerService()

mQueue=newKeyQ();

因为WindowManagerService.java(frameworks/base/services/java/com/android/server)中有:

privateclassKeyQextendsKeyInputQueueimplementsKeyInputQueue.FilterCallback

KeyQ是抽象类KeyInputQueue的实现,所以newKeyQ类的时候实际上在KeyInputQueue类中创建了一个线程InputDeviceReader专门用来从设备读取按键事件.

代码:

ThreadmThread=newThread("InputDeviceReader" ){

publicvoid
run(){

//在循环中调用:
    readEvent(ev);
...
send
=
preprocessEvent(di,ev);
//实际调用的是KeyQ类的preprocessEvent函数
...
intkeycode=
rotateKeyCodeLocked(ev.keycode);
int[]map= mKeyRotationMap;

for(inti=0;i<N;i+=2
){

if(map==
keyCode)

returnmap[i+1
];

}
//


addLocked(di,curTime,ev.flags,RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di,di.mDownTime,curTime,down,keycode,
0 ,scancode,...));

QueuedEventev
=
obtainLocked(device,when,flags,classType,event);
}
};

readEvent()实际上调用的是com_android_server_KeyInputQueue.cpp(frameworks/base/services/jni)中的

staticjbooleanandroid_server_KeyInputQueue_readEvent(JNIEnv*env,jobjectclazz,jobjectevent)来读取事件,

boolres=hub-& gt;getEvent(&deviceId,&type,&scancode,&keycode,&flags,&value,&when) 调用的是EventHub.cpp(frameworks/base/libs/ui)中的:

boolEventHub::getEvent(int32_t*outDeviceId,int32_t*outType,

int32_t*outScancode,int32_t*outKeycode,uint32_t*outFlags,

int32_t*outValue,nsecs_t*outWhen)

在函数中调用了读设备操作:res=read(mFDs.fd,&iev,sizeof(iev));

在构造函数WindowManagerService()调用newKeyQ()以后接着调用了:

mInputThread=newInputDispatcherThread();
...

mInputThread.start();

来启动一个线程InputDispatcherThread

run()
process();
QueuedEventev=mQueue.getEvent(...)

因为WindowManagerService类中:finalKeyQmQueue;

所以实际上InputDispatcherThread线程实际上从KeyQ的事件队列中读取按键事件,在process()方法中进行处理事件。

switch(ev.classType)
caseRawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event,0,0);
mQueue.recycleEvent(ev);
break;
caseRawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG,"Readnextevent"+ev);
dispatchPointer(ev,(MotionEvent)ev.event,0,0);
break;

 caseRawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev,(MotionEvent)ev.event,0,0);
break;

===============================================================

补充一些内容:

在写程序时,需要捕获KEYCODE_HOME、KEYCODE_ENDCALL、KEYCODE_POWER这几个按键,但是这几个按键系统做了特殊处理,

在进行dispatch之前做了一些操作,HOME除了Keygaurd之外,不分发给任何其他APP,ENDCALL和POWER也类似,所以需要我们系统

处理之前进行处理。

我的做法是自己定义一个FLAG,在自己的程序中添加此FLAG,然后在WindowManagerServices.java中获取当前窗口的FLAG属性,如果是我

们自己设置的那个FLAG,则不进行特殊处理,直接分发按键消息到我们的APP当中,由APP自己处理。

这部分代码最好添加在

@Override
booleanpreprocessEvent(InputDevicedevice,RawInputEventevent)

方法中,这个方法是KeyInputQueue中的一个虚函数,在处理按键事件之前的一个“预处理”。

PS:对HOME键的处理好像必需要修改PhoneWindowManager.java中的interceptKeyTi方法,具体可以参考对KeyGuard程序的处理。

更多相关文章

  1. Unity调用Android配置方法
  2. Android(安卓)Power Management
  3. Android应用程序启动过程源代码分析
  4. 利用HTML5开发Android
  5. unity与Android相互调用
  6. android WebView总结
  7. Android截屏浅析
  8. Android属性之build.prop,及property_get/property_set && Androi
  9. Linux/Android——input系统之 kernel层 与 frameworks层交互 (

随机推荐

  1. html中文本域选中后会出现蓝边框
  2. 如何使用CSS将表放在页面的中心?
  3. js限制文本框只能输入数字方法
  4. jquery validate (jquery 验证的三种方式
  5. JQUERY组装对象并调用自身函数改变自己的
  6. jquery获取input值的各种情况
  7. jQuery File Upload 单页面多实例的实现
  8. jquery每个添加类中间延迟
  9. Jquery ajax回调函数不执行
  10. 有什么方法可以让jQuery循环在到达终点或