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文件中:
[java] view plain copy
- 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文件中:
[java] view plain copy
- 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文件中:
[java] view plain copy
- 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文件中:
[java] view plain copy
- 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文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- publicclassInputManager{
- ......
- publicvoidstart(){
- Slog.i(TAG,"Startinginputmanager");
- nativeStart();
- }
- ......
- }
Step 11. InputManager.nativeStart
这个函数定义在frameworks/base/services/jni$ vi com_android_server_InputManager.cpp文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- boolInputDispatcherThread::threadLoop(){
- mDispatcher->dispatchOnce();
- returntrue;
- }
Step 14. InputDispatcher.dispatchOnce
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- boolInputReaderThread::threadLoop(){
- mReader->loopOnce();
- returntrue;
- }
Step 17. InputReader.loopOnce
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- boolEventHub::openPlatformInput(void)
- {
- ......
- res=scanDir(device_path);
- if(res<0){
- LOGE("scandirfailedfor%s\n",device_path);
- }
- returntrue;
- }
[cpp] view plain copy
- staticconstchar*device_path="/dev/input";
Step 20.EventHub.scanDir
这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中:
[cpp] view plain copy
- 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文件中:
[cpp] view plain copy
- 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;
- }
[cpp] view plain copy
- fd=open(deviceName,O_RDWR);
[cpp] view plain copy
- mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
- if(mDevicesById[devid].seq==0){
- mDevicesById[devid].seq=1<<SEQ_SHIFT;
- }
[cpp] view plain copy
- 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中:
[cpp] view plain copy
- mFDs[mFDCount].fd=fd;
- mFDs[mFDCount].events=POLLIN;
- mFDs[mFDCount].revents=0;
[cpp] view plain copy
- //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;
- }
- }
- }
如果是键盘设备,初始化工作还未完成,还要继续设置键盘的布局等信息:
[cpp] view plain copy
- 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循环里面,首先会检查当前是否有输入设备被关闭,如果有,就返回一个设备移除的事件给调用方:
[cpp] view plain copy
- //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;
- }
[cpp] view plain copy
- 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;
- }
[cpp] view plain copy
- if(mNeedToSendFinishedDeviceScan){
- mNeedToSendFinishedDeviceScan=false;
- outEvent->type=FINISHED_DEVICE_SCAN;
- outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC);
- returntrue;
- }
[cpp] view plain copy
- //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;
- }
- }
- }
[cpp] view plain copy
- intpollResult=poll(mFDs,mFDCount,-1);
这里的mFDs包含了我们所要监控的输入设备的打开文件描述符,这是在前面的openPlatformInput函数中初始化的。
Step 22. poll
这是一个Linux系统的文件操作系统调用,它用来查询指定的文件列表是否有有可读写的,如果有,就马上返回,否则的话,就阻塞线程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。在我们的这个场景中,就是要查询是否有键盘事件发生,如果有的话,就返回,否则的话,当前线程就睡眠等待键盘事件的发生了。
这样,InputManager的启动过程就分析完了,下面我们再分析应用程序注册键盘消息接收通道的过程。
2.应用程序注册键盘消息接收通道的过程分析
InputManager启动以后,就开始负责监控键盘输入事件了。当InputManager监控到键盘输入事件时,它应该把这个键盘事件分发给谁呢?当然是要把这个键盘消息分发给当前激活的Activity窗口了,不过,当前激活的Activity窗口还需要主动注册一个键盘消息接收通道到InputManager中去,InputManager才能把这个键盘消息分发给它处理。那么,当前被激活的Activity窗口又是什么时候去注册这个键盘消息接收通道的呢?在前面一篇文章Android应用程序启动过程源代码分析中,我们分析Android应用程序的启动过程时,在Step 33中分析到ActivityThread类的handleLaunchActivity函数中,我们曾经说过,当函数handleLaunchActivity调用performLaunchActivity函数来加载这个完毕应用程序的默认Activity后,再次回到handleLaunchActivity函数时,会调用handleResumeActivity函数来使这个Activity进入Resumed状态。在调用handleResumeActivity函数的过程中,ActivityThread会通过android.view.WindowManagerImpl类为该Activity创建一个ViewRoot实例,并且会通过调用ViewRoot类的setView成员函数把与该Activity关联的View设置到这个ViewRoot中去,而Activity正是通过ViewRoot类的setView成员函数来注册键盘消息接收通道的。
有了这些背影知识后,接下来,我们就可以从ViewRoot.setView函数开始分析应用程序注册键盘消息接收通道的过程了。首先看一下这个注册过程的序列图,然后再详细分析每一个步骤:
Step 1. ViewRoot.setView
这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:
[java] view plain copy- publicfinalclassViewRootextendsHandlerimplementsViewParent,
- View.AttachInfo.Callbacks{
- ......
- publicvoidsetView(Viewview,WindowManager.LayoutParamsattrs,
- ViewpanelParentView){
- ......
- synchronized(this){
- if(mView==null){
- ......
- //Schedulethefirstlayout-before-addingtothewindow
- //manager,tomakesurewedotherelayoutbeforereceiving
- //anyothereventsfromthesystem.
- requestLayout();
- mInputChannel=newInputChannel();
- try{
- res=sWindowSession.add(mWindow,mWindowAttributes,
- getHostVisibility(),mAttachInfo.mContentInsets,
- mInputChannel);
- }catch(RemoteExceptione){
- ......
- }finally{
- ......
- }
- ......
- if(viewinstanceofRootViewSurfaceTaker){
- mInputQueueCallback=
- ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
- }
- if(mInputQueueCallback!=null){
- mInputQueue=newInputQueue(mInputChannel);
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- }else{
- InputQueue.registerInputChannel(mInputChannel,mInputHandler,
- Looper.myQueue());
- }
- ......
- }
- }
- }
- }
这个函数中与注册键盘消息接收通道(InputChannel)相关的逻辑主要有三处,一是调用requestLayout函数来通知InputManager,这个Activity窗口是当前被激活的窗口,二是调用sWindowSession(WindowManagerService内部类Session的远程接口)的add成员函数来把键盘消息接收通道的一端注册在InputManager中,三是调用InputQueue的registerInputChannel成员函数来把键盘消息接收通道的另一端注册在本应用程序的消息循环(Looper)中。这样,当InputManager监控到有键盘消息时,就会先找到当前被激活的窗口,然后找到其在InputManager中对应的键盘消息接收通道,通过这个通道在InputManager中的一端来通知在应用程序消息循环中的另一端,就把键盘消息分发给当前激活的Activity窗口了。
在接下来的内容中,我们首先描述requestLayout函数是如何告诉InputManager当前的Activity窗口便是激活窗口的,接着再回过头来分析应用程序是如何把键盘消息接收通道的一端注册到InputManager中去的,最后分析应用程序是如何键盘消息接收通道的另一端注册到本应用程序的消息循环中去了。
Step 2.ViewRoot.requestLayout
这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中:
[java] view plain copy
- publicfinalclassViewRootextendsHandlerimplementsViewParent,
- View.AttachInfo.Callbacks{
- ......
- publicvoidrequestLayout(){
- ......
- mLayoutRequested=true;
- scheduleTraversals();
- }
- ......
- }
Step 3.WindowManagerService.Session.relayout
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
[java] view plain copy
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- privatefinalclassSessionextendsIWindowSession.Stub
- implementsIBinder.DeathRecipient{
- ......
- publicintrelayout(IWindowwindow,WindowManager.LayoutParamsattrs,
- intrequestedWidth,intrequestedHeight,intviewFlags,
- booleaninsetsPending,RectoutFrame,RectoutContentInsets,
- RectoutVisibleInsets,ConfigurationoutConfig,SurfaceoutSurface){
- //Log.d(TAG,">>>>>>ENTEREDrelayoutfrom"+Binder.getCallingPid());
- intres=relayoutWindow(this,window,attrs,
- requestedWidth,requestedHeight,viewFlags,insetsPending,
- outFrame,outContentInsets,outVisibleInsets,outConfig,outSurface);
- //Log.d(TAG,"<<<<<<EXITINGrelayoutto"+Binder.getCallingPid());
- returnres;
- }
- ......
- }
- ......
- }
这个函数只是简单地调用WindowManagerService的成员函数relayoutWIndow来进一步处理。
Step 4.WindowManagerService.relayoutWIndow
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- publicintrelayoutWindow(Sessionsession,IWindowclient,
- WindowManager.LayoutParamsattrs,intrequestedWidth,
- intrequestedHeight,intviewVisibility,booleaninsetsPending,
- RectoutFrame,RectoutContentInsets,RectoutVisibleInsets,
- ConfigurationoutConfig,SurfaceoutSurface){
- ......
- synchronized(mWindowMap){
- ......
- mInputMonitor.updateInputWindowsLw();
- }
- ......
- }
- ......
- }
Step 5.InputMonitor.updateInputWindowsLw
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
[java] view plain copy
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- finalclassInputMonitor{
- ......
- /*Updatesthecachedwindowinformationprovidedtotheinputdispatcher.*/
- publicvoidupdateInputWindowsLw(){
- //Populatetheinputwindowlistwithinformationaboutallofthewindowsthat
- //couldpotentiallyreceiveinput.
- //Asanoptimization,wecouldtrytoprunethelistofwindowsbutthisturns
- //outtobedifficultbecauseonlythenativecodeknowsforsurewhichwindow
- //currentlyhastouchfocus.
- finalArrayList<WindowState>windows=mWindows;
- finalintN=windows.size();
- for(inti=N-1;i>=0;i--){
- finalWindowStatechild=windows.get(i);
- if(child.mInputChannel==null||child.mRemoved){
- //Skipthiswindowbecauseitcannotpossiblyreceiveinput.
- continue;
- }
- ......
- //Addawindowtoourlistofinputwindows.
- finalInputWindowinputWindow=mTempInputWindows.add();
- ......
- }
- //Sendwindowstonativecode.
- mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
- ......
- }
- ......
- }
- ......
- }
Step 6.InputManager.setInputWindows
这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
[java] view plain copy
- publicclassInputManager{
- ......
- publicvoidsetInputWindows(InputWindow[]windows){
- nativeSetInputWindows(windows);
- }
- ......
- }
Step 7.InputManager.nativeSetInputWindows
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:
[cpp] view plain copy
- staticvoidandroid_server_InputManager_nativeSetInputWindows(JNIEnv*env,jclassclazz,
- jobjectArraywindowObjArray){
- if(checkInputManagerUnitialized(env)){
- return;
- }
- gNativeInputManager->setInputWindows(env,windowObjArray);
- }
Step 8.NativeInputManager.setInputWindows
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中:
[cpp] view plain copy
- voidNativeInputManager::setInputWindows(JNIEnv*env,jobjectArraywindowObjArray){
- Vector<InputWindow>windows;
- jsizelength=env->GetArrayLength(windowObjArray);
- for(jsizei=0;i<length;i++){
- jobjectinputTargetObj=env->GetObjectArrayElement(windowObjArray,i);
- if(!inputTargetObj){
- break;//foundnullelementindicatingendofusedportionofthearray
- }
- windows.push();
- InputWindow&window=windows.editTop();
- boolvalid=populateWindow(env,inputTargetObj,window);
- if(!valid){
- windows.pop();
- }
- env->DeleteLocalRef(inputTargetObj);
- }
- mInputManager->getDispatcher()->setInputWindows(windows);
- }
Step 9. InputDispatcher.setInputWindows
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
[cpp] view plain copy
- voidInputDispatcher::setInputWindows(constVector<InputWindow>&inputWindows){
- ......
- {//acquirelock
- AutoMutex_l(mLock);
- //Clearoldwindowpointers.
- sp<InputChannel>oldFocusedWindowChannel;
- if(mFocusedWindow){
- oldFocusedWindowChannel=mFocusedWindow->inputChannel;
- mFocusedWindow=NULL;
- }
- mWindows.clear();
- //Loopovernewwindowsandrebuildthenecessarywindowpointersfor
- //trackingfocusandtouch.
- mWindows.appendVector(inputWindows);
- size_tnumWindows=mWindows.size();
- for(size_ti=0;i<numWindows;i++){
- constInputWindow*window=&mWindows.itemAt(i);
- if(window->hasFocus){
- mFocusedWindow=window;
- break;
- }
- }
- ......
- }//releaselock
- ......
- }
这样,InputManager就把当前激活的Activity窗口保存在InputDispatcher中了,后面就可以把键盘消息分发给它来处理。
回到Step 1中的ViewRoot.setView函数中,接下来就调用下面语句来注册键盘消息接收通道的一端到InputManager中去:
[java] view plain copy
- mInputChannel=newInputChannel();
- try{
- res=sWindowSession.add(mWindow,mWindowAttributes,
- getHostVisibility(),mAttachInfo.mContentInsets,
- mInputChannel);
- }catch(RemoteExceptione){
- ......
- }finally{
- ......
- }
Step 10.WindowManagerService.Session.add
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
[java] view plain copy- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- privatefinalclassSessionextendsIWindowSession.Stub
- implementsIBinder.DeathRecipient{
- ......
- publicintadd(IWindowwindow,WindowManager.LayoutParamsattrs,
- intviewVisibility,RectoutContentInsets,InputChanneloutInputChannel){
- returnaddWindow(this,window,attrs,viewVisibility,outContentInsets,
- outInputChannel);
- }
- ......
- }
- ......
- }
Step 11.WindowManagerService.addWindow
这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中:
[java] view plain copy
- publicclassWindowManagerServiceextendsIWindowManager.Stub
- implementsWatchdog.Monitor{
- ......
- publicintaddWindow(Sessionsession,IWindowclient,
- WindowManager.LayoutParamsattrs,intviewVisibility,
- RectoutContentInsets,InputChanneloutInputChannel){
- ......
- WindowStatewin=null;
- synchronized(mWindowMap){
- ......
- win=newWindowState(session,client,token,
- attachedWindow,attrs,viewVisibility);
- ......
- if(outInputChannel!=null){
- Stringname=win.makeInputChannelName();
- InputChannel[]inputChannels=InputChannel.openInputChannelPair(name);
- win.mInputChannel=inputChannels[0];
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
- mInputManager.registerInputChannel(win.mInputChannel);
- }
- ......
- }
- ......
- }
- ......
- }
这里的outInputChannel即为前面在Step 1中创建的InputChannel,它不为NULL,因此,这里会通过InputChannel.openInputChannelPair函数来创建一对输入通道,其中一个位于WindowManagerService中,另外一个通过outInputChannel参数返回到应用程序中:
[java] view plain copy
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
创建输入通道之前,WindowManagerService会为当前Activity窗口创建一个WindowState对象win,用来记录这个Activity窗口的状态信息。当创建这对输入管道成功以后,也会把其中的一个管道保存在这个WindowState对象win的成员变量mInputChannel中,后面要注销这个管道的时候,就是从这个WindownState对象中取回这个管道的:
[java] view plain copy
- win.mInputChannel=inputChannels[0];
接下来我们就看一下InputChannel.openInputChannelPair函数的实现。
Step 12.InputChannel.openInputChannelPair
这个函数定义在frameworks/base/core/java/android/view/InputChannel.java文件中:
- publicfinalclassInputChannelimplementsParcelable{
- ......
- /**
- *Createsanewinputchannelpair.Onechannelshouldbeprovidedtotheinput
- *dispatcherandtheothertotheapplication'sinputqueue.
- *@paramnameThedescriptive(non-unique)nameofthechannelpair.
- *@returnApairofinputchannels.Theyaresymmetricandindistinguishable.
- */
- publicstaticInputChannel[]openInputChannelPair(Stringname){
- ......
- returnnativeOpenInputChannelPair(name);
- }
- ......
- }
Step 13.InputChannel.nativeOpenInputChannelPair
这个函数定义在frameworks/base/core/jni/android_view_InputChannel.cpp文件中:
- staticjobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv*env,
- jclassclazz,jstringnameObj){
- constchar*nameChars=env->GetStringUTFChars(nameObj,NULL);
- String8name(nameChars);
- env->ReleaseStringUTFChars(nameObj,nameChars);
- sp<InputChannel>serverChannel;
- sp<InputChannel>clientChannel;
- status_tresult=InputChannel::openInputChannelPair(name,serverChannel,clientChannel);
- if(result){
- LOGE("Couldnotopeninputchannelpair.status=%d",result);
- jniThrowRuntimeException(env,"Couldnotopeninputchannelpair.");
- returnNULL;
- }
- //TODOmorerobusterrorchecking
- jobjectserverChannelObj=android_view_InputChannel_createInputChannel(env,
- newNativeInputChannel(serverChannel));
- jobjectclientChannelObj=android_view_InputChannel_createInputChannel(env,
- newNativeInputChannel(clientChannel));
- jobjectArraychannelPair=env->NewObjectArray(2,gInputChannelClassInfo.clazz,NULL);
- env->SetObjectArrayElement(channelPair,0,serverChannelObj);
- env->SetObjectArrayElement(channelPair,1,clientChannelObj);
- returnchannelPair;
- }
Step 14.InputChannel.openInputChannelPair
这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中:
[cpp] view plain copy
- status_tInputChannel::openInputChannelPair(constString8&name,
- sp<InputChannel>&outServerChannel,sp<InputChannel>&outClientChannel){
- status_tresult;
- intserverAshmemFd=ashmem_create_region(name.string(),DEFAULT_MESSAGE_BUFFER_SIZE);
- if(serverAshmemFd<0){
- ......
- }else{
- result=ashmem_set_prot_region(serverAshmemFd,PROT_READ|PROT_WRITE);
- if(result<0){
- ......
- }else{
- //Dupthefiledescriptorbecausetheserverandclientinputchannelobjectsthat
- //arereturnedmayhavedifferentlifetimesbuttheysharethesamesharedmemoryregion.
- intclientAshmemFd;
- clientAshmemFd=dup(serverAshmemFd);
- if(clientAshmemFd<0){
- ......
- }else{
- intforward[2];
- if(pipe(forward)){
- ......
- }else{
- intreverse[2];
- if(pipe(reverse)){
- ......
- }else{
- String8serverChannelName=name;
- serverChannelName.append("(server)");
- outServerChannel=newInputChannel(serverChannelName,
- serverAshmemFd,reverse[0],forward[1]);
- String8clientChannelName=name;
- clientChannelName.append("(client)");
- outClientChannel=newInputChannel(clientChannelName,
- clientAshmemFd,forward[0],reverse[1]);
- returnOK;
- }
- ......
- }
- ......
- }
- }
- }
- ......
- }
[cpp] view plain copy
- InputChannel::InputChannel(constString8&name,int32_tashmemFd,int32_treceivePipeFd,
- int32_tsendPipeFd):
- mName(name),mAshmemFd(ashmemFd),mReceivePipeFd(receivePipeFd),mSendPipeFd(sendPipeFd){
- ......
- }
具体来说,Server端和Client端的InputChannel分别是这样构成的:
Server Input Channel: ashmem - reverse(read) - forward(write)
Client Input Channel: ashmem - forward(read) - reverse(write)
前面我们在Android应用程序消息处理机制(Looper、Handler)分析一文中学习Android应用程序的消息处理机制时知道,管道可以用作进程间通信,其中一个进程在管道的读端等待新的内空可读,另一个进程在管道的写端写入新的内容以唤醒在管道读端等待的进程,这样就实现了进程间通信。在我们这个情景中,Client端可以在前向管道(forward pipe)的读端睡眠等待新的内容可读,而Server端可以通过向前向管道(forward pipe)的写端写入新的内容来唤醒Client端,同样,把前向管道(forward pipe)换成反向管道(reverse pipe),也能实现Client端唤醒Server端。在后面我们分析InputDispatcher分发键盘消息时,会看到它们的用法。
有了这些背景知识后,相信上面的openInputChannelPair的代码就容易理解了,这里就不再详述了。
创建好了这两个输入通道后,回到Step 11中的WindowManagerService.addWindow函数中,一方面它把刚才创建的Client端的输入通道通过outInputChannel参数返回到应用程序中:
[java] view plain copy
- inputChannels[1].transferToBinderOutParameter(outInputChannel);
另一方面,它还要把刚才创建的Server端的输入通道注册到InputManager中:
[java] view plain copy
- mInputManager.registerInputChannel(win.mInputChannel);
这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中:
[java] view plain copy
- publicclassInputManager{
- ......
- /**
- *Registersaninputchannelsothatitcanbeusedasaninputeventtarget.
- *@paraminputChannelTheinputchanneltoregister.
- */
- publicvoidregisterInputChannel(InputChannelinputChannel){
- if(inputChannel==null){
- thrownewIllegalArgumentException("inputChannelmustnotbenull.");
- }
- nativeRegisterInputChannel(inputChannel,false);
- }
- ......
- }
Step 16.InputManager.nativeRegisterInputChannel
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
[cpp] view plain copy
- staticvoidandroid_server_InputManager_nativeRegisterInputChannel(JNIEnv*env,jclassclazz,
- jobjectinputChannelObj,jbooleanmonitor){
- ......
- sp<InputChannel>inputChannel=android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- status_tstatus=gNativeInputManager->registerInputChannel(
- env,inputChannel,inputChannelObj,monitor);
- ......
- }
Step 17. NativeInputManager.registerInputChannel
这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp 文件中:
[cpp] view plain copy
- status_tNativeInputManager::registerInputChannel(JNIEnv*env,
- constsp<InputChannel>&inputChannel,jobjectinputChannelObj,boolmonitor){
- ......
- status=mInputManager->getDispatcher()->registerInputChannel(inputChannel,monitor);
- ......
- }
Step 18.InputDispatcher.registerInputChannel
这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中:
[cpp] view plain copy
- status_tInputDispatcher::registerInputChannel(constsp<InputChannel>&inputChannel,boolmonitor){
- ......
- {//acquirelock
- AutoMutex_l(mLock);
- if(getConnectionIndexLocked(inputChannel)>=0){
- LOGW("Attemptedtoregisteralreadyregisteredinputchannel'%s'",
- inputChannel->getName().string());
- returnBAD_VALUE;
- }
- sp<Connection>connection=newConnection(inputChannel);
- status_tstatus=connection->initialize();
- if(status){
- LOGE("Failedtoinitializeinputpublisherforinputchannel'%s',status=%d",
- inputChannel->getName().string(),status);
- returnstatus;
- }
- int32_treceiveFd=inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd,connection);
- if(monitor){
- mMonitoringChannels.push(inputChannel);
- }
- mLooper->addFd(receiveFd,0,ALOOPER_EVENT_INPUT,handleReceiveCallback,this);
- runCommandsLockedInterruptible();
- }//releaselock
- returnOK;
- }
道的读端描述符。回忆一下Step 14中的InputChannel.openInputChannelPair函数,我们创建了一个Server端的InputChannel,就是对应这里的inputChannel了,这个inputChannel的Receive Pipe Fd就是我们前面说的反向管道的读端描述符了。有了这个Receive Pipe Fd后,就以它作为Key值来把前面创建的Connection对象保存在InputDispatcher中,这样就基本完成键盘消息接收通道的注册了。但是,注册的工作还未完成,最后,还要把这个Receive Pipe Fd添加到InputDispatcher的成员变量mLooper中去,这里的成员变量mLooper的类型为Looper,我们在前面介绍InputManager的启动过程的Step 15中已经见过了,这里就不再详述了,不过这里仍然值得介绍一下它的addFd函数。
在前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析中,我们在介绍到Android应用程序的消息循环一节时,曾经说过,在Looper类内部,会创建一个管道,然后Looper会睡眠在这个管道的读端,等待另外一个线程来往这个管道的写端写入新的内容,从而唤醒等待在这个管道读端的线程,除此之外,Looper还可以同时睡眠等待在其它的文件描述符上,因为它是通过Linux系统的epoll机制来批量等待指定的文件有新的内容可读的。这些其它的文件描述符就是通过Looper类的addFd成函数添加进去的了,在添加的时候,还可以指定回调函数,即当这个文件描述符所指向的文件有新的内容可读时,Looper就会调用这个hanldeReceiveCallback函数,有兴趣的读者可以自己研究一下Looper类的addFd函数的实现,它位于frameworks/base/libs/utils/Looper.cpp文件中。
分析到这里,Server端的InputChannel就注册完成了。回忆一下前面介绍InputManager启动过程的Step 14,这时InputDispatcherThread同时睡眠在InputDispatcher的成员变量mLooper内部的管道的读端以及这里的Server端InputChannel里面的反向管道的读端上,mLooper内部的管道的读端等待键盘事件的发生而被唤醒,而Server端InputChannel里面的反向管道的读端等待Client端InputChannel里面的反向管道的写端被写入新的内容而被唤醒。
Server端的InputChannel注册完成后,回到Step 11中的WindowManagerService.addWindow函数,接下来就是把Client端的InputChannel转换成addWindow的参数outInputChannel中,然后返回到Step 1中的ViewRoot.setView函数中,继续执行Client端的InputChannel的注册过程,即为应用程序这一侧注册键盘消息接收通道:
[java] view plain copy
- if(viewinstanceofRootViewSurfaceTaker){
- mInputQueueCallback=
- ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
- }
- if(mInputQueueCallback!=null){
- mInputQueue=newInputQueue(mInputChannel);
- mInputQueueCallback.onInputQueueCreated(mInputQueue);
- }else{
- InputQueue.registerInputChannel(mInputChannel,mInputHandler,
- Looper.myQueue());
- }
这里的变量view一般不为RootViewSurfaceTaker的实例,因此,最后会执行下面语句:
[java] view plain copy
- InputQueue.registerInputChannel(mInputChannel,mInputHandler,
- Looper.myQueue());
Step 19. InputQueue.registerInputChannel
这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中:
[java] view plain copy
- publicfinalclassInputQueue{
- ......
- publicstaticvoidregisterInputChannel(InputChannelinputChannel,InputHandlerinputHandler,
- MessageQueuemessageQueue){
- ......
- synchronized(sLock){
- ......
- nativeRegisterInputChannel(inputChannel,inputHandler,messageQueue);
- }
- }
- ......
- }
Step 20.InputQueue.nativeRegisterInputChannel
这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
[cpp] view plain copy
- staticvoidandroid_view_InputQueue_nativeRegisterInputChannel(JNIEnv*env,jclassclazz,
- jobjectinputChannelObj,jobjectinputHandlerObj,jobjectmessageQueueObj){
- status_tstatus=gNativeInputQueue.registerInputChannel(
- env,inputChannelObj,inputHandlerObj,messageQueueObj);
- ......
- }
Step 21. NativeInputQueue.registerInputChannel
这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中:
[cpp] view plain copy
- status_tNativeInputQueue::registerInputChannel(JNIEnv*env,jobjectinputChannelObj,
- jobjectinputHandlerObj,jobjectmessageQueueObj){
- sp<InputChannel>inputChannel=android_view_InputChannel_getInputChannel(env,
- inputChannelObj);
- ......
- sp<Looper>looper=android_os_MessageQueue_getLooper(env,messageQueueObj);
- {//acquirelock
- AutoMutex_l(mLock);
- if(getConnectionIndex(inputChannel)>=0){
- LOGW("Attemptedtoregisteralreadyregisteredinputchannel'%s'",
- inputChannel->getName().string());
- returnBAD_VALUE;
- }
- uint16_tconnectionId=mNextConnectionId++;
- sp<Connection>connection=newConnection(connectionId,inputChannel,looper);
- status_tresult=connection->inputConsumer.initialize();
- if(result){
- LOGW("Failedtoinitializeinputconsumerforinputchannel'%s',status=%d",
- inputChannel->getName().string(),result);
- returnresult;
- }
- connection->inputHandlerObjGlobal=env->NewGlobalRef(inputHandlerObj);
- int32_treceiveFd=inputChannel->getReceivePipeFd();
- mConnectionsByReceiveFd.add(receiveFd,connection);
- looper->addFd(receiveFd,0,ALOOPER_EVENT_INPUT,handleReceiveCallback,this);
- }//releaselock
- ......
- returnOK;
- }
介绍到这里,应用程序注册键盘消息接收通道的过程就分析完成了。这个过程比较复杂,这里小结一下:
A. 即将会被激活的Activity窗口,会通知InputManager,它是当前激活的窗口,因此,一旦发生键盘事件的时候,InputManager就把这个键盘事件抛给这个Activity处理;
B. 应用程序会为这个Activity窗口和InputManager之间创建一个键盘消息接收通道,这个通道的一端由一个Server端的InputChannel构成,另一端由Client端的InputChannel构成,Server端的InputChannel注册在由InputManager所管理的InputDispatcher中,而Client端的InputChannel注册在由应用程序主线程的消息循环对象Looper中;
C. 注册在InputDispatcher中的InputChannel由一个反向管道的读端和一个前向管道的写端组成,而注册在应用程序主线程的消息循环对象Looper中的InputChannel由这个前向管道的读端和反向管道的写端组成,这种交叉结构使得当有键盘事件发生时,InputDispatcher可以把这个事件通知给应用程序。
应用程序注册好键盘消息接收通道后,接下来就开始分析InputManager分发键盘消息给应用程序的过程了。
3.InputManager分发键盘消息给应用程序的过程分析
在分析InputManager分发键盘消息给应用程序的过程之前,我们先假设现在没有键盘事件发生,因此,InputManager中的InputReader正在睡眠等待键盘事件的发生,而InputManager中的InputDispatcher正在等待InputReader从睡眠中醒过来并且唤醒它,而应用程序也正在消息循环中等待InputDispatcher从睡眠中醒过来并且唤醒它。这时候,用户按下键盘中的一个键,于是,一系列唤醒的事件就依次发生了,一直到应用程序中正在显示的Activity得到通知,有键盘事件发生了。我们先来看这个过程的序列图,然后再详细分析每一个步骤:
Step 1. InputReader.pollOnce
Step 2. EventHub.getEvent
这两个函数分别定义在frameworks/base/libs/ui/InputReader.cpp和frameworks/base/libs/ui/EventHub.cpp文件中,前面我们在分析InputManager的启动过程的Step 17和Step 18时,已经看到过这两个函数了。InputReaderThread线程会不民地循环调用InputReader.pollOnce函数来读入键盘事件,而实际的键盘事件读入操作是由EventHub.getEvent函数来进行的。如果当前没有键盘事件发生,InputReaderThread线程就会睡眠在EventHub.getEvent函数上,而当键盘事件发生后,就会把这个事件封装成一个RawEvent对象,然后返回到pollOnce函数中,执行process函数进一步处理:
[cpp] view plain copy
- voidInputReader::loopOnce(){
- RawEventrawEvent;
- mEventHub->getEvent(&rawEvent);
- ......
- process(&rawEvent);
- }
这个函数定义在frameworks/base/libs/ui/InputReader.cpp文件中:
[cpp] view plain copy
- voidInputReader::process(constRawEvent*rawEvent){
- switch(rawEvent->type){
- caseEventHubInterface::DEVICE_ADDED:
- addDevice(rawEvent->deviceId);
- break;
更多相关文章
- Android线程间通信机制——深入理解 Looper、Handler、Message
- Android(安卓)Handler 异步消息处理机制的妙用 创建强大的图片加
- 很容易理解的Android消息机制分析
- Android(安卓)Handler 异步消息处理机制的妙用 创建强大的图片加
- Android消息处理-概念普及篇
- Handler 机制(一)
- Android的init过程(二):初始化语言(init.rc)解析
- Android(安卓)推送通知指南
- 箭头函数的基础使用