Android应用程序键盘(Keyboard)消息处理机制分析(一)
在Android系统中,键盘按键事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序处理,不过和普通消息不一样,它是由硬件中断触发的;在上一篇文章《Android应用程序消息处理机制(Looper、Handler)分析》中,我们分析了Android应用程序的消息处理机制,本文将结合这种消息处理机制来详细分析Android应用程序是如何获得键盘按键消息的。
在系统启动的时候,SystemServer会启动窗口管理服务WindowManagerService,WindowManagerService在启动的时候就会通过系统输入管理器InputManager来总负责监控键盘消息。这些键盘消息一般都是分发给当前**的Activity窗口来处理的,因此,当前**的Activity窗口在创建的时候,会到WindowManagerService中去注册一个接收键盘消息的通道,表明它要处理键盘消息,而当InputManager监控到有键盘消息时,就会分给给它处理。当当前**的Activity窗口不再处于**状态时,它也会到WindowManagerService中去反注册之前的键盘消息接收通道,这样,InputManager就不会再把键盘消息分发给它来处理。
由于本文的内容比较多,在接下面的章节中,我们将分为五个部分来详细描述Android应用程序获得键盘按键消息的过程,每一个部分都是具体描述键盘消息处理过程中的一个过程。结合上面的键盘消息处理框架,这四个过程分别是InputManager的启动过程、应用程序注册键盘消息接收通道的过程、InputManager分发键盘消息给应用程序的过程以及应用程序注销键盘消息接收通道的过程。为了更好地理解Android应用程序获得键盘按键消息的整个过程,建议读者首先阅读Android应用程序消息处理机制(Looper、Handler)分析一文,理解了Android应用程序的消息处理机制后,就能很好的把握本文的内容。
1. InputManager的启动过程分析
前面说过,Android系统的键盘事件是由InputManager来监控的,而InputManager是由窗口管理服务WindowManagerService来启动的。
从前面一篇文章Android系统进程Zygote启动过程的源代码分析中,我们知道在Android系统中,Zygote进程负责启动系统服务进程SystemServer,而系统服务进程SystemServer负责启动系统中的各种关键服务,例如我们在前面两篇文章Android应用程序安装过程源代码分析和Android系统默认Home应用程序(Launcher)的启动过程源代码分析中提到的Package管理服务PackageManagerService和Activity管理服务ActivityManagerService。这里我们所讨论的窗口管理服务WindowManagerService也是由SystemServer来启动的,具体的启动过程这里就不再详述了,具体可以参考PackageManagerService和ActivityManagerService的启动过程。
了解了WindowManagerService的启动过程之后,我们就可以继续分析InputManager的启动过程了。我们先来看一下InputManager启动过程的序列图,然后根据这个序列图来一步步分析它的启动过程:
Step 1. WindowManagerService.main
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件
中:
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- publicstaticWindowManagerServicemain(Contextcontext,
- PowerManagerServicepm,booleanhaveInputMethods){
- WMThreadthr=newWMThread(context,pm,haveInputMethods);
- thr.start();
- synchronized(thr){
- while(thr.mService==null){
- try{
- thr.wait();
- }catch(InterruptedExceptione){
- }
- }
- returnthr.mService;
- }
- }
- ......
- }
Step 2. WMThread.run
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- staticclassWMThreadextendsThread{
- ......
- publicvoidrun(){
- ......
- WindowManagerServices=newWindowManagerService(mContext,mPM,
- mHaveInputMethods);
- ......
- }
- }
- ......
- }
Step 3. WindowManagerService<init>
WindowManagerService类的构造函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- finalInputManagermInputManager;
- ......
- privateWindowManagerService(Contextcontext,PowerManagerServicepm,
- booleanhaveInputMethods){
- ......
- mInputManager=newInputManager(context,this);
- ......
- mInputManager.start();
- ......
- }
- ......
- }
Step 4. InputManager<init>@java
Java层的InputManager类的构造函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- publicclassInputManager{
- ......
- publicInputManager(Contextcontext,WindowManagerServicewindowManagerService){
- this.mContext=context;
- this.mWindowManagerService=windowManagerService;
- this.mCallbacks=newCallbacks();
- init();
- }
- ......
- }
Step 5. InputManager.init
这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- publicclassInputManager{
- ......
- privatevoidinit(){
- Slog.i(TAG,"Initializinginputmanager");
- nativeInit(mCallbacks);
- }
- ......
- }
Step 6. InputManager.nativeInit
这个函数定义在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- staticvoidandroid_server_InputManager_nativeInit(JNIEnv*env,jclassclazz,
- jobjectcallbacks){
- if(gNativeInputManager==NULL){
- gNativeInputManager=newNativeInputManager(callbacks);
- }else{
- LOGE("Inputmanageralreadyinitialized.");
- jniThrowRuntimeException(env,"Inputmanageralreadyinitialized.");
- }
- }
Step 7. NativeInputManager<init>
NativeInputManager类的构造函数定义在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- NativeInputManager::NativeInputManager(jobjectcallbacksObj):
- mFilterTouchEvents(-1),mFilterJumpyTouchEvents(-1),mVirtualKeyQuietTime(-1),
- mMaxEventsPerSecond(-1),
- mDisplayWidth(-1),mDisplayHeight(-1),mDisplayOrientation(ROTATION_0){
- JNIEnv*env=jniEnv();
- mCallbacksObj=env->NewGlobalRef(callbacksObj);
- sp<EventHub>eventHub=newEventHub();
- mInputManager=newInputManager(eventHub,this,this);
- }
Step 8. InputManager<init>@C++
C++层的InputManager类的构造函数定义在frameworks/base/libs/ui/InputManager.cpp文件中:
- InputManager::InputManager(
- constsp<EventHubInterface>&eventHub,
- constsp<InputReaderPolicyInterface>&readerPolicy,
- constsp<InputDispatcherPolicyInterface>&dispatcherPolicy){
- mDispatcher=newInputDispatcher(dispatcherPolicy);
- mReader=newInputReader(eventHub,readerPolicy,mDispatcher);
- initialize();
- }
Step 9. InputManager.initialize
这个函数定义在frameworks/base/libs/ui/InputManager.cpp文件中:
- voidInputManager::initialize(){
- mReaderThread=newInputReaderThread(mReader);
- mDispatcherThread=newInputDispatcherThread(mDispatcher);
- }
至此,InputManager的初始化工作就完成了,在回到Step 3中继续分析InputManager的进一步启动过程之前,我们先来作一个小结,看看这个初始化过程都做什么事情:
A. 在Java层中的WindowManagerService中创建了一个InputManager对象,由它来负责管理Android应用程序框架层的键盘消息处理;
B. 在C++层也相应地创建一个InputManager本地对象来负责监控键盘事件;
C. 在C++层中的InputManager对象中,分别创建了一个InputReader对象和一个InputDispatcher对象,前者负责读取系统中的键盘消息,后者负责把键盘消息分发出去;
D.InputReader对象和一个InputDispatcher对象分别是通过InputReaderThread线程实例和InputDispatcherThread线程实例来实键盘消息的读取和分发的。
有了这些对象之后,万事就俱备了,回到Step 3中,调用InputManager类的start函数来执行真正的启动操作。
Step 10. InputManager.start
这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
- publicclassInputManager{
- ......
- publicvoidstart(){
- Slog.i(TAG,"Startinginputmanager");
- nativeStart();
- }
- ......
- }
Step 11. InputManager.nativeStart
这个函数定义在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
- staticvoidandroid_server_InputManager_nativeStart(JNIEnv*env,jclassclazz){
- if(checkInputManagerUnitialized(env)){
- return;
- }
- status_tresult=gNativeInputManager->getInputManager()->start();
- if(result){
- jniThrowRuntimeException(env,"Inputmanagercouldnotbestarted.");
- }
- }
Step 12. InputManager.start
这个函数定义在frameworks/base/libs/ui/InputManager.cpp文件中:
- status_tInputManager::start(){
- status_tresult=mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);
- if(result){
- LOGE("CouldnotstartInputDispatcherthreadduetoerror%d.",result);
- returnresult;
- }
- result=mReaderThread->run("InputReader",PRIORITY_URGENT_DISPLAY);
- if(result){
- LOGE("CouldnotstartInputReaderthreadduetoerror%d.",result);
- mDispatcherThread->requestExit();
- returnresult;
- }
- returnOK;
- }
我们先来分析InputDispatcherThread线程分发消息的过程,然后再回过头来分析InputReaderThread线程读取消息的过程。
Step 13.InputDispatcherThread.threadLoop
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- boolInputDispatcherThread::threadLoop(){
- mDispatcher->dispatchOnce();
- returntrue;
- }
Step 14. InputDispatcher.dispatchOnce
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
- voidInputDispatcher::dispatchOnce(){
- nsecs_tkeyRepeatTimeout=mPolicy->getKeyRepeatTimeout();
- nsecs_tkeyRepeatDelay=mPolicy->getKeyRepeatDelay();
- nsecs_tnextWakeupTime=LONG_LONG_MAX;
- {//acquirelock
- AutoMutex_l(mLock);
- dispatchOnceInnerLocked(keyRepeatTimeout,keyRepeatDelay,&nextWakeupTime);
- if(runCommandsLockedInterruptible()){
- nextWakeupTime=LONG_LONG_MIN;//forcenextpolltowakeupimmediately
- }
- }//releaselock
- //Waitforcallbackortimeoutorwake.(makesureweroundup,notdown)
- nsecs_tcurrentTime=now();
- int32_ttimeoutMillis;
- if(nextWakeupTime>currentTime){
- uint64_ttimeout=uint64_t(nextWakeupTime-currentTime);
- timeout=(timeout+999999LL)/1000000LL;
- timeoutMillis=timeout>INT_MAX?-1:int32_t(timeout);
- }else{
- timeoutMillis=0;
- }
- mLooper->pollOnce(timeoutMillis);
- }
Step 15. Looper.pollOnce
这个函数定义在frameworks/base/libs/utils/Looper.cpp文件中,具体可以参考前面Android应用程序消息处理机制(Looper、Handler)分析一文,这里就不再详述了。总的来说,就是在Looper类中,会创建一个管道,当调用Looper类的pollOnce函数时,如果管道中没有内容可读,那么当前线程就会进入到空闲等待状态;当有键盘事件发生时,InputReader就会往这个管道中写入新的内容,这样就会唤醒前面正在等待键盘事件发生的线程。
InputDispatcher类分发消息的过程就暂时分析到这里,后面会有更进一步的分析,现在,我们回到Step 12中,接着分析InputReader类读取键盘事件的过程。在调用了InputReaderThread线程类的run就函数后,同样会进入到InputReaderThread线程类的threadLoop函数中去。
Step 16. InputReaderThread.threadLoop
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
- boolInputReaderThread::threadLoop(){
- mReader->loopOnce();
- returntrue;
- }
Step 17. InputReader.loopOnce
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
- voidInputReader::loopOnce(){
- RawEventrawEvent;
- mEventHub->getEvent(&rawEvent);
- #ifDEBUG_RAW_EVENTS
- LOGD("Inputevent:device=0x%xtype=0x%xscancode=%dkeycode=%dvalue=%d",
- rawEvent.deviceId,rawEvent.type,rawEvent.scanCode,rawEvent.keyCode,
- rawEvent.value);
- #endif
- process(&rawEvent);
- }
Step 18. EventHub.getEvent
这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中:
- boolEventHub::getEvent(RawEvent*outEvent)
- {
- outEvent->deviceId=0;
- outEvent->type=0;
- outEvent->scanCode=0;
- outEvent->keyCode=0;
- outEvent->flags=0;
- outEvent->value=0;
- outEvent->when=0;
- //NotethatweonlyallowonecallertogetEvent(),sodon'tneed
- //todolockinghere...onlywhenadding/removingdevices.
- if(!mOpened){
- mError=openPlatformInput()?NO_ERROR:UNKNOWN_ERROR;
- mOpened=true;
- mNeedToSendFinishedDeviceScan=true;
- }
- for(;;){
- //Reportanydevicesthathadlastbeenadded/removed.
- if(mClosingDevices!=NULL){
- device_t*device=mClosingDevices;
- LOGV("Reportingdeviceclosed:id=0x%x,name=%s\n",
- device->id,device->path.string());
- mClosingDevices=device->next;
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=DEVICE_REMOVED;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- deletedevice;
- mNeedToSendFinishedDeviceScan=true;
- returntrue;
- }
- if(mOpeningDevices!=NULL){
- device_t*device=mOpeningDevices;
- LOGV("Reportingdeviceopened:id=0x%x,name=%s\n",
- device->id,device->path.string());
- mOpeningDevices=device->next;
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=DEVICE_ADDED;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- mNeedToSendFinishedDeviceScan=true;
- returntrue;
- }
- if(mNeedToSendFinishedDeviceScan){
- mNeedToSendFinishedDeviceScan=false;
- outEvent->type=FINISHED_DEVICE_SCAN;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- returntrue;
- }
- //Grabthenextinputevent.
- for(;;){
- //Consumebufferedinputevents,ifany.
- if(mInputBufferIndex<mInputBufferCount){
- conststructinput_event&iev=mInputBufferData[mInputBufferIndex++];
- constdevice_t*device=mDevices[mInputDeviceIndex];
- LOGV("%sgot:t0=%d,t1=%d,type=%d,code=%d,v=%d",device->path.string(),
- (int)iev.time.tv_sec,(int)iev.time.tv_usec,iev.type,iev.code,iev.value);
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=iev.type;
- outEvent->scanCode=iev.code;
- if(iev.type==EV_KEY){
- status_terr=device->layoutMap->map(iev.code,
- &outEvent->keyCode,&outEvent->flags);
- LOGV("iev.code=%dkeyCode=%dflags=0x%08xerr=%d\n",
- iev.code,outEvent->keyCode,outEvent->flags,err);
- if(err!=0){
- outEvent->keyCode=AKEYCODE_UNKNOWN;
- outEvent->flags=0;
- }
- }else{
- outEvent->keyCode=iev.code;
- }
- outEvent->value=iev.value;
- //Useaneventtimestampinthesametimebaseas
- //java.lang.System.nanoTime()andandroid.os.SystemClock.uptimeMillis()
- //asexpectedbytherestofthesystem.
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- returntrue;
- }
- //Finishreadingalleventsfromdevicesidentifiedinpreviouspoll().
- //ThiscodeassumesthatmInputDeviceIndexisinitially0andthatthe
- //reventsmemberofpollfdisinitializedto0whenthedeviceisfirstadded.
- //SincemFDs[0]isusedforinotify,weprocessregulareventsstartingatindex1.
- mInputDeviceIndex+=1;
- if(mInputDeviceIndex>=mFDCount){
- break;
- }
- conststructpollfd&pfd=mFDs[mInputDeviceIndex];
- if(pfd.revents&POLLIN){
- int32_treadSize=read(pfd.fd,mInputBufferData,
- sizeof(structinput_event)*INPUT_BUFFER_SIZE);
- if(readSize<0){
- if(errno!=EAGAIN&&errno!=EINTR){
- LOGW("couldnotgetevent(errno=%d)",errno);
- }
- }elseif((readSize%sizeof(structinput_event))!=0){
- LOGE("couldnotgetevent(wrongsize:%d)",readSize);
- }else{
- mInputBufferCount=readSize/sizeof(structinput_event);
- mInputBufferIndex=0;
- }
- }
- }
- ......
- mInputDeviceIndex=0;
- //Pollforevents.Mindthewakelockdance!
- //Weholdawakelockatalltimesexceptduringpoll().Thisworksduetosome
- //subtlechoreography.Whenadevicedriverhaspending(unread)events,itacquires
- //akernelwakelock.However,oncethelastpendingeventhasbeenread,thedevice
- //driverwillreleasethekernelwakelock.Topreventthesystemfromgoingtosleep
- //whenthishappens,theEventHubholdsontoitsownuserwakelockwhiletheclient
- //isprocessingevents.Thusthesystemcanonlysleepiftherearenoevents
- //pendingorcurrentlybeingprocessed.
- release_wake_lock(WAKE_LOCK_ID);
- intpollResult=poll(mFDs,mFDCount,-1);
- acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_ID);
- if(pollResult<=0){
- if(errno!=EINTR){
- LOGW("pollfailed(errno=%d)\n",errno);
- usleep(100000);
- }
- }
- }
- }
首先,如果是第一次进入到这个函数中时,成员变量mOpened的值为false,于是就会调用openPlatformInput函数来打开系统输入设备,在本文中,我们主要讨论的输入设备就是键盘了。打开了这些输入设备文件后,就可以对这些输入设备进行是监控了。如果不是第一次进入到这个函数,那么就会分析当前有没有输入事件发生,如果有,就返回这个事件,否则就会进入等待状态,等待下一次输入事件的发生。在我们这个场景中,就是等待下一次键盘事件的发生了。
我们先分析openPlatformInput函数的实现,然后回过头来分析这个getEvent函数的具体的实现。
Step 19. EventHub.openPlatformInput
这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中:
- boolEventHub::openPlatformInput(void)
- {
- ......
- res=scanDir(device_path);
- if(res<0){
- LOGE("scandirfailedfor%s\n",device_path);
- }
- returntrue;
- }
- staticconstchar*device_path="/dev/input";
Step 20.EventHub.scanDir
这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中:
- intEventHub::scanDir(constchar*dirname)
- {
- chardevname[PATH_MAX];
- char*filename;
- DIR*dir;
- structdirent*de;
- dir=opendir(dirname);
- if(dir==NULL)
- return-1;
- strcpy(devname,dirname);
- filename=devname+strlen(devname);
- *filename++='/';
- while((de=readdir(dir))){
- if(de->d_name[0]=='.'&&
- (de->d_name[1]=='\0'||
- (de->d_name[1]=='.'&&de->d_name[2]=='\0')))
- continue;
- strcpy(filename,de->d_name);
- openDevice(devname);
- }
- closedir(dir);
- return0;
- }
Step 21.EventHub.openDevice
这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中:
- intEventHub::openDevice(constchar*deviceName){
- intversion;
- intfd;
- structpollfd*new_mFDs;
- device_t**new_devices;
- char**new_device_names;
- charname[80];
- charlocation[80];
- charidstr[80];
- structinput_idid;
- LOGV("Openingdevice:%s",deviceName);
- AutoMutex_l(mLock);
- fd=open(deviceName,O_RDWR);
- if(fd<0){
- LOGE("couldnotopen%s,%s\n",deviceName,strerror(errno));
- return-1;
- }
- ......
- intdevid=0;
- while(devid<mNumDevicesById){
- if(mDevicesById[devid].device==NULL){
- break;
- }
- devid++;
- }
- ......
- mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if(mDevicesById[devid].seq==0){
- mDevicesById[devid].seq=1<<SEQ_SHIFT;
- }
- new_mFDs=(pollfd*)realloc(mFDs,sizeof(mFDs[0])*(mFDCount+1));
- new_devices=(device_t**)realloc(mDevices,sizeof(mDevices[0])*(mFDCount+1));
- if(new_mFDs==NULL||new_devices==NULL){
- LOGE("outofmemory");
- return-1;
- }
- mFDs=new_mFDs;
- mDevices=new_devices;
- ......
- device_t*device=newdevice_t(devid|mDevicesById[devid].seq,deviceName,name);
- if(device==NULL){
- LOGE("outofmemory");
- return-1;
- }
- device->fd=fd;
- mFDs[mFDCount].fd=fd;
- mFDs[mFDCount].events=POLLIN;
- mFDs[mFDCount].revents=0;
- //Figureoutthekindsofeventsthedevicereports.
- uint8_tkey_bitmask[sizeof_bit_array(KEY_MAX+1)];
- memset(key_bitmask,0,sizeof(key_bitmask));
- LOGV("Gettingkeys...");
- if(ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(key_bitmask)),key_bitmask)>=0){
- //Seeifthisisakeyboard.Ignoreeverythinginthebuttonrangeexceptfor
- //gamepadswhicharealsoconsideredkeyboards.
- if(containsNonZeroByte(key_bitmask,0,sizeof_bit_array(BTN_MISC))
- ||containsNonZeroByte(key_bitmask,sizeof_bit_array(BTN_GAMEPAD),
- sizeof_bit_array(BTN_DIGI))
- ||containsNonZeroByte(key_bitmask,sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX+1))){
- device->classes|=INPUT_DEVICE_CLASS_KEYBOARD;
- 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;
- }
- }
- }
- ......
- if((device->classes&INPUT_DEVICE_CLASS_KEYBOARD)!=0){
- 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;
- }
- status_tstatus=device->layoutMap->load(keylayoutFilename);
- if(status){
- LOGE("Error%dloadingkeylayout.",status);
- }
- //telltheworldaboutthedevname(thedescriptivename)
- if(!mHaveFirstKeyboard&&!defaultKeymap&&strstr(name,"-keypad")){
- //thebuilt-inkeyboardhasawell-knowndeviceIDof0,
- //thisdevicebetternotgoaway.
- mHaveFirstKeyboard=true;
- mFirstKeyboardId=device->id;
- property_set("hw.keyboards.0.devname",name);
- }else{
- //ensuremFirstKeyboardIdissetto-something-.
- if(mFirstKeyboardId==0){
- mFirstKeyboardId=device->id;
- }
- }
- charpropName[100];
- sprintf(propName,"hw.keyboards.%u.devname",device->id);
- property_set(propName,name);
- //'Q'keysupport=cheaptestofwhetherthisisanalpha-capablekbd
- if(hasKeycodeLocked(device,AKEYCODE_Q)){
- device->classes|=INPUT_DEVICE_CLASS_ALPHAKEY;
- }
- //SeeifthisdevicehasaDPAD.
- if(hasKeycodeLocked(device,AKEYCODE_DPAD_UP)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_DOWN)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_LEFT)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_RIGHT)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_CENTER)){
- device->classes|=INPUT_DEVICE_CLASS_DPAD;
- }
- //Seeifthisdevicehasagamepad.
- for(size_ti=0;i<sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]);i++){
- if(hasKeycodeLocked(device,GAMEPAD_KEYCODES[i])){
- device->classes|=INPUT_DEVICE_CLASS_GAMEPAD;
- break;
- }
- }
- LOGI("Newkeyboard:device->id=0x%xdevname='%s'propName='%s'keylayout='%s'\n",
- device->id,name,propName,keylayoutFilename);
- }
- ......
- mDevicesById[devid].device=device;
- device->next=mOpeningDevices;
- mOpeningDevices=device;
- mDevices[mFDCount]=device;
- mFDCount++;
- return0;
- }
- fd=open(deviceName,O_RDWR);
- mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if(mDevicesById[devid].seq==0){
- mDevicesById[devid].seq=1<<SEQ_SHIFT;
- }
- mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if(mDevicesById[devid].seq==0){
- mDevicesById[devid].seq=1<<SEQ_SHIFT;
- }
- new_mFDs=(pollfd*)realloc(mFDs,sizeof(mFDs[0])*(mFDCount+1));
- new_devices=(device_t**)realloc(mDevices,sizeof(mDevices[0])*(mFDCount+1));
- if(new_mFDs==NULL||new_devices==NULL){
- LOGE("outofmemory");
- return-1;
- }
- mFDs=new_mFDs;
- mDevices=new_devices;
- ......
- device_t*device=newdevice_t(devid|mDevicesById[devid].seq,deviceName,name);
- if(device==NULL){
- LOGE("outofmemory");
- return-1;
- }
- device->fd=fd;
同时,这个设备文件还会保存在数组mFDs中:
- mFDs[mFDCount].fd=fd;
- mFDs[mFDCount].events=POLLIN;
- mFDs[mFDCount].revents=0;
- //Figureoutthekindsofeventsthedevicereports.
- uint8_tkey_bitmask[sizeof_bit_array(KEY_MAX+1)];
- memset(key_bitmask,0,sizeof(key_bitmask));
- LOGV("Gettingkeys...");
- if(ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(key_bitmask)),key_bitmask)>=0){
- //Seeifthisisakeyboard.Ignoreeverythinginthebuttonrangeexceptfor
- //gamepadswhicharealsoconsideredkeyboards.
- if(containsNonZeroByte(key_bitmask,0,sizeof_bit_array(BTN_MISC))
- ||containsNonZeroByte(key_bitmask,sizeof_bit_array(BTN_GAMEPAD),
- sizeof_bit_array(BTN_DIGI))
- ||containsNonZeroByte(key_bitmask,sizeof_bit_array(KEY_OK),
- sizeof_bit_array(KEY_MAX+1))){
- device->classes|=INPUT_DEVICE_CLASS_KEYBOARD;
- 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;
- }
- }
- }
如果是键盘设备,初始化工作还未完成,还要继续设置键盘的布局等信息:
- if((device->classes&INPUT_DEVICE_CLASS_KEYBOARD)!=0){
- 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;
- }
- status_tstatus=device->layoutMap->load(keylayoutFilename);
- if(status){
- LOGE("Error%dloadingkeylayout.",status);
- }
- //telltheworldaboutthedevname(thedescriptivename)
- if(!mHaveFirstKeyboard&&!defaultKeymap&&strstr(name,"-keypad")){
- //thebuilt-inkeyboardhasawell-knowndeviceIDof0,
- //thisdevicebetternotgoaway.
- mHaveFirstKeyboard=true;
- mFirstKeyboardId=device->id;
- property_set("hw.keyboards.0.devname",name);
- }else{
- //ensuremFirstKeyboardIdissetto-something-.
- if(mFirstKeyboardId==0){
- mFirstKeyboardId=device->id;
- }
- }
- charpropName[100];
- sprintf(propName,"hw.keyboards.%u.devname",device->id);
- property_set(propName,name);
- //'Q'keysupport=cheaptestofwhetherthisisanalpha-capablekbd
- if(hasKeycodeLocked(device,AKEYCODE_Q)){
- device->classes|=INPUT_DEVICE_CLASS_ALPHAKEY;
- }
- //SeeifthisdevicehasaDPAD.
- if(hasKeycodeLocked(device,AKEYCODE_DPAD_UP)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_DOWN)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_LEFT)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_RIGHT)&&
- hasKeycodeLocked(device,AKEYCODE_DPAD_CENTER)){
- device->classes|=INPUT_DEVICE_CLASS_DPAD;
- }
- //Seeifthisdevicehasagamepad.
- for(size_ti=0;i<sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]);i++){
- if(hasKeycodeLocked(device,GAMEPAD_KEYCODES[i])){
- device->classes|=INPUT_DEVICE_CLASS_GAMEPAD;
- break;
- }
- }
- LOGI("Newkeyboard:device->id=0x%xdevname='%s'propName='%s'keylayout='%s'\n",
- device->id,name,propName,keylayoutFilename);
- }
回到Step 18中,我们继续分析EventHub.getEvent函数的实现。
在中间的for循环里面,首先会检查当前是否有输入设备被关闭,如果有,就返回一个设备移除的事件给调用方:
- //Reportanydevicesthathadlastbeenadded/removed.
- if(mClosingDevices!=NULL){
- device_t*device=mClosingDevices;
- LOGV("Reportingdeviceclosed:id=0x%x,name=%s\n",
- device->id,device->path.string());
- mClosingDevices=device->next;
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=DEVICE_REMOVED;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- deletedevice;
- mNeedToSendFinishedDeviceScan=true;
- returntrue;
- }
- if(mOpeningDevices!=NULL){
- device_t*device=mOpeningDevices;
- LOGV("Reportingdeviceopened:id=0x%x,name=%s\n",
- device->id,device->path.string());
- mOpeningDevices=device->next;
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=DEVICE_ADDED;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- mNeedToSendFinishedDeviceScan=true;
- returntrue;
- }
- if(mNeedToSendFinishedDeviceScan){
- mNeedToSendFinishedDeviceScan=false;
- outEvent->type=FINISHED_DEVICE_SCAN;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- returntrue;
- }
- //Grabthenextinputevent.
- for(;;){
- //Consumebufferedinputevents,ifany.
- if(mInputBufferIndex<mInputBufferCount){
- conststructinput_event&iev=mInputBufferData[mInputBufferIndex++];
- constdevice_t*device=mDevices[mInputDeviceIndex];
- LOGV("%sgot:t0=%d,t1=%d,type=%d,code=%d,v=%d",device->path.string(),
- (int)iev.time.tv_sec,(int)iev.time.tv_usec,iev.type,iev.code,iev.value);
- if(device->id==mFirstKeyboardId){
- outEvent->deviceId=0;
- }else{
- outEvent->deviceId=device->id;
- }
- outEvent->type=iev.type;
- outEvent->scanCode=iev.code;
- if(iev.type==EV_KEY){
- status_terr=device->layoutMap->map(iev.code,
- &outEvent->keyCode,&outEvent->flags);
- LOGV("iev.code=%dkeyCode=%dflags=0x%08xerr=%d\n",
- iev.code,outEvent->keyCode,outEvent->flags,err);
- if(err!=0){
- outEvent->keyCode=AKEYCODE_UNKNOWN;
- outEvent->flags=0;
- }
- }else{
- outEvent->keyCode=iev.code;
- }
- outEvent->value=iev.value;
- //Useaneventtimestampinthesametimebaseas
- //java.lang.System.nanoTime()andandroid.os.SystemClock.uptimeMillis()
- //asexpectedbytherestofthesystem.
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- returntrue;
- }
- //Finishreadingalleventsfromdevicesidentifiedinpreviouspoll().
- //ThiscodeassumesthatmInputDeviceIndexisinitially0andthatthe
- //reventsmemberofpollfdisinitializedto0whenthedeviceisfirstadded.
- //SincemFDs[0]isusedforinotify,weprocessregulareventsstartingatindex1.
- mInputDeviceIndex+=1;
- if(mInputDeviceIndex>=mFDCount){
- break;
- }
- conststructpollfd&pfd=mFDs[mInputDeviceIndex];
- if(pfd.revents&POLLIN){
- int32_treadSize=read(pfd.fd,mInputBufferData,
- sizeof(structinput_event)*INPUT_BUFFER_SIZE);
- if(readSize<0){
- if(errno!=EAGAIN&&errno!=EINTR){
- LOGW("couldnotgetevent(errno=%d)",errno);
- }
- }elseif((readSize%sizeof(structinput_event))!=0){
- LOGE("couldnotgetevent(wrongsize:%d)",readSize);
- }else{
- mInputBufferCount=readSize/sizeof(structinput_event);
- mInputBufferIndex=0;
- }
- }
- }
- intpollResult=poll(mFDs,mFDCount,-1);
这里的mFDs包含了我们所要监控的输入设备的打开文件描述符,这是在前面的openPlatformInput函数中初始化的。
Step 22. poll
这是一个Linux系统的文件操作系统调用,它用来查询指定的文件列表是否有有可读写的,如果有,就马上返回,否则的话,就阻塞线程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。在我们的这个场景中,就是要查询是否有键盘事件发生,如果有的话,就返回,否则的话,当前线程就睡眠等待键盘事件的发生了。
这样,InputManager的启动过程就分析完了,下面我们再分析应用程序注册键盘消息接收通道的过程。