Android按键事件传递流程(一)

做Android开发的少不了对触摸、按键事件进行处理,对于手机来说,主要是手势移动、触摸按下等,而TV主要通过遥控器、按键操作,按键事件不同于触摸事件,必须先获得焦点,然后才能移动、选择。

Android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层,内核层传递过程不在本文研究范围内,本文主要对按键事件在Framework层、应用层的传递过程进行分析(本文基于Android5.1.1版本),带着问题出发:

1.    Framework层如何获得输入事件

2.    Framework层获得按键事件后如何处理、存储

3.    Framework层如何分发、传递给应用层

4.    Framework层服务端管道和应用程序客户端管道如何创建、注册

5.    应用层如何从Framework层接收按键事件

6.    应用层接收到按键事件后如何传递

7.    特殊按键如何处理

8.    总结

1.    Framework层如何获得输入事件

要分析输入系统,最好先从输入事件系统服务InputManagerService入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。

1.1    InputManagerService的初始化

Android系统启动时,Android部分第一个用户空间进程zygote(注:如果从Linux角度出发,第一个用户空间进程是init)首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerService、PowerManagerService、PackageManagerService、InputManagerService等,相关代码在SystemServer.java的run方法中,与InputManagerService相关部分:

1) inputManager = new InputManagerService(context);
 
2) wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
 
3) ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 
4) inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
 
5) inputManager.start();

1.1.1    InputManagerService的构造方法

this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象

mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

用Looper中的消息队列作为参数,调用本地方法nativeInit,返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。

LocalServices.addService(InputManagerInternal.class, new LocalService());

把InputManagerInternal.class和LocalService对象作为一对映射添加到ArrayMap, Object>中

1.1.2    nativeInit方法实现

static jlongnativeInit(JNIEnv* env, jclassclazz,
        jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast(im);
}

android_os_MessageQueue_getMessageQueue方法:

sp android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
    jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
    return reinterpret_cast(ptr);
}

获得了C++层NativeMessageQueue对象,NativeMessageQueue对象是在创建Looper对象时创建的,在本博客Handler机制中已经分析过

NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
 messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast(im);

创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;getLooper()获得c++层的Looper对象,在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中

NativeInputManager的构造函数:

NativeInputManager::NativeInputManager(jobjectcontextObj,
        jobjectserviceObj, const sp& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
 
    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);
 
    {
        AutoMutex_l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }
 
    sp eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法;创建c++层中全局变量mContextObj, mServiceObj;创建EventHub对象并作为参数创建InputManager对象。

1.1.3    EventHub.cpp的构造函数

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
//    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
 
    mINotifyFd = inotify_init();
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);
 
    struct epoll_eventeventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
 
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
 
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);
 
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);
 
    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
 
    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kernel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

初始化必要的变量,采用epoll机制系统调用接口epoll_create创建epoll对象,返回文件描述符mEpollFd,代表epoll对象;采用inotify机制监听文件或目录的移动、读取、写入或删除等事件,inotify机制主要包含2个系统调用接口:inotify_init, inotify_add_watch

inotify_init:创建一个inotify对象,如果成功,返回一个文件描述符,作为该对象;返回-1表示出错

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)

inotify_add_watch:把监控项添加到mINotifyFd对象的监控列表中,第二个参数DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;第三个参数是一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。IN_DELETE:当被监控目录内删除文件或目录时触发该事件;IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。

如果inotify_add_watch执行成功,返回一个非负监控描述符(假如为wd),代表被监控的项,如果将来不需要再监听了,可以使用inotify_rm_watch(fd, wd)删除所添加的监控项wd。

struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

把inotify对象mINotifyFd添加到epoll对象的兴趣列表中,此处采用inotify与epoll机制结合起来检查文件

eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

创建了管道对象读取端文件描述符mWakeReadPipeFd并添加到epoll对象的兴趣列表中

读到此处,提出2个问题:

Q1    epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait,猜测在代码某处肯定有epoll_wait,否则epoll机制无法工作

Q2    如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用

记住这两个问题,有助于在分析多进程控制时的代码走向

1.1.4    InputManager.cpp的构造器

mInputManager = new InputManager(eventHub, this, this);
InputManager::InputManager(
    const sp& eventHub,
    const sp& readerPolicy,
    const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

第一个参数是刚创建的EventHub对象,第二、三个参数都是this,当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager

用这两个传递过去的对象类型作为参数分别创建了InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象

1.1.5    InputDispatcher的构造函数

InputDispatcher::InputDispatcher(const sp& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    mLooper = new Looper(false);
 
    mKeyRepeatState.lastKeyEntry = NULL;
 
    policy->getDispatcherConfiguration(&mConfig);
}

把传递过来的NativeInputManager对象赋给mPolicy,该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型,然后又初始化了很多变量,其中包括:

mLooper = new Looper(false);

创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。

注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。

Q3    既然有了epoll_ctl,那肯定某处会调用epoll_wait

1.1.6    InputReader的构造函数

InputReader::InputReader(const sp& eventHub,
        const sp& policy,
        const sp& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

第一个参数是EventHub对象,第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型

mQueuedListener = new QueuedInputListener(listener);

把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量

1.1.7    InputManager.cpp的initialize函数

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

用InputReader对象作为参数创建InputReaderThread线程对象,用InputDispatcher作为参数创建InputDispatcherThread线程对象,InputReaderThread的构造函数:

InputReaderThread::InputReaderThread(const sp& reader) :
    Thread(/*canCallJava*/ true), mReader(reader) {
}

把InputReader对象赋给mReader变量

InputDispatcherThread的构造函数:

InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) :
    Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

把InputDispatcher对象赋给mDispatcher变量

进程执行到此处,InputManagerService的初始化就完成,整个过程都是在创建、初始化各种对象。

Q4    既然创建了线程对象,那么就应该在某处启动线程,否则线程无法运行,在哪里启动线程

1.2    WindowManagerService的main方法

wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
public static WindowManagerServicemain(final Contextcontext,
 final InputManagerServiceim,
 final boolean haveInputMethods, final boolean showBootMsgs,
 final boolean onlyCore) {
    final WindowManagerService[] holder = new WindowManagerService[1];
    DisplayThread.getHandler().runWithScissors(new Runnable() {
        @Override
 public void run() {
    holder[0] = new WindowManagerService(context, im,
 haveInputMethods, showBootMsgs, onlyCore);
 }
    }, 0);
 return holder[0];
}
mInputManager = inputManager;

main方法创建了WindowManagerService对象,第二个参数是InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager

1.3    ServiceManager的addService方法

ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

把InputManagerService服务注册到ServiceManager中

1.4    InputManagerService的setWindowManagerCallbacks方法

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

把InputMonitor对象传递到InputManagerService中去,方便回调,比如7.2节通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法

1.5     InputManagerService的start方法

start方法中有两句:

nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);

nativeStart的本地实现:

static void nativeStart(JNIEnv* env, jclassclazz, jlongptr) {
    NativeInputManager* im = reinterpret_cast(ptr);
 
    status_tresult = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

ptr在1.1.1节已经提到过,是NativeInputManager对象地址,只不过是长整形,此处再转化成NativeInputManager型指针

Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调

Q5    创建了看门狗,有什么用?

nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart  —->  InputManager.cpp的start()

1.5.1    InputManager.cpp的start

status_tInputManager::start() {
    status_tresult = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
 
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);
 
        mDispatcherThread->requestExit();
        return result;
    }
 
    return OK;
}

首先启动分发器线程InputDispatcherThread,调用run方法后,开始执行threadLoop方法,如果threadLoop返回true,就再次执行threadLoop方法直到requestExit方法停止线程;再调用InputReaderThread的run方法启动接收器线程。这回答了1.1.7节Q4问题,只要创建了线程,那就应该有启动线程的地方。

threadLoop中调用了mDispatcher->dispatchOnce()

1.5.2    InputDispatcher.cpp的dispatchOnce

void InputDispatcher::dispatchOnce() {
    nsecs_tnextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex_l(mLock);
        mDispatcherIsAliveCondition.broadcast();
 
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
 
        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock
 
    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_tcurrentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}
if (!haveCommandsLocked()) {
    dispatchOnceInnerLocked(&nextWakeupTime);
}

一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked

1.5.2.1    InputDispatcher.cpp的dispatchOnceInnerLocked

if (! mPendingEvent) {
    if (mInboundQueue.isEmpty()) {
 if (isAppSwitchDue) {
 // The inbound queue is empty so the app switch key we were waiting
 // for will never arrive.  Stop waiting for it.
 resetPendingAppSwitchLocked(false);
 isAppSwitchDue = false;
 }
 
 // Synthesize a key repeat if appropriate.
 if (mKeyRepeatState.lastKeyEntry) {
 if (currentTime >= mKeyRepeatState.nextRepeatTime) {
 mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
 } else {
 if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
 *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
 }
 }
 }
 
 // Nothing to do if there is no pending event.
 if (!mPendingEvent) {
 return;
 }
    } else {
 // Inbound queue has at least one entry.
 mPendingEvent = mInboundQueue.dequeueAtHead();
 traceInboundQueueLengthLocked();
    }
 
    // Poke user activity for this event.
    if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
 pokeUserActivityLocked(mPendingEvent);
    }
 
    // Get ready to dispatch the event.
    resetANRTimeoutsLocked();
}

mPendingEvent初始化为空,mInboundQueue队列也为空,下面语句:

if (! mPendingEvent) {
    if (mInboundQueue.isEmpty()) {

成立,进入到if语句中

if (isAppSwitchDue) {
    // The inbound queue is empty so the app switch key we were waiting
    // for will never arrive.  Stop waiting for it.
    resetPendingAppSwitchLocked(false);
    isAppSwitchDue = false;
}

如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false

mKeyRepeatState.lastKeyEntry初始化为false,跳过:

if (mKeyRepeatState.lastKeyEntry) {

这条语句,继续:

if (!mPendingEvent) {
    return;
}

mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回到dispatchOnce方法中继续后面语句的执行:

if (runCommandsLockedInterruptible()) {
    nextWakeupTime = LONG_LONG_MIN;
}

一开始,没有命令待处理,runCommandsLockedInterruptible返回false,跳过语句;如果有命令的话,就取出来处理

1.5.2.2    Looper的pollOnce

mLooper->pollOnce(timeoutMillis);

看到熟悉的语句了,pollOnce一直执行到epoll_wait监控管道读取端,如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;如果还没有事件,进程InputDispatcherThread在此等待,这就回答了Q3

Q6    既然有Looper的pollOnce方法,就有对应的唤醒方法,在哪里?

1.5.3    InputReader.cpp的loopOnce

void InputReader::loopOnce() {
    int32_toldGeneration;
    int32_ttimeoutMillis;
    bool inputDevicesChanged = false;
    Vector inputDevices;
    { // acquire lock
        AutoMutex_l(mLock);
 
        oldGeneration = mGeneration;
        timeoutMillis = -1;
 
        uint32_tchanges = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock
 
    size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
    { // acquire lock
        AutoMutex_l(mLock);
        mReaderIsAliveCondition.broadcast();
 
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
 
        if (mNextTimeout != LLONG_MAX) {
            nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }
 
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
 
    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
 
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener->flush();
}

InputDeviceInfo:描述输入设备特征和特性的类

getEvents用来获取输入设备信息、输入事件;processEventsLocked用来处理获得的事件的;按照顺序,首先进入到getEvents

1.5.3.1-1    EventHub.cpp的getEvents函数

size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_tbufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
 
    AutoMutex_l(mLock);
 
    struct input_eventreadBuffer[bufferSize];
 
    RawEvent* event = buffer;
    size_tcapacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
 
        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;
 
            ALOGI("Reopening all input devices due to a configuration change.");
 
            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }
 
        // Report any devices that had last been added/removed.
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting device closed: id=%d, name=%s\n",
                device->id, device->path.string());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            deletedevice;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
 
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
 
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                device->id, device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
 
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
 
        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }
 
            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_tnRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }
 
            ssize_tdeviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }
 
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                int32_treadSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                            " bufferSize: %zu capacity: %zu errno: %d)\n",
                            device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
                    size_tcount = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
                                device->path.string(),
                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type, iev.code, iev.value);
 
                        // Some input devices may have a better concept of the time
                        // when an input event was actually generated than the kernel
                        // which simply timestamps all events on entry to evdev.
                        // This is a custom Android extension of the input protocol
                        // mainly intended for use with uinput based device drivers.
                        if (iev.type == EV_MSC) {
                            if (iev.code == MSC_ANDROID_TIME_SEC) {
                                device->timestampOverrideSec = iev.value;
                                continue;
                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                                device->timestampOverrideUsec = iev.value;
                                continue;
                            }
                        }
                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
                            iev.time.tv_sec = device->timestampOverrideSec;
                            iev.time.tv_usec = device->timestampOverrideUsec;
                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
                                device->timestampOverrideSec = 0;
                                device->timestampOverrideUsec = 0;
                            }
                            ALOGV("applied override time %d.%06d",
                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
                        }
 
#ifdef HAVE_POSIX_CLOCKS
                        // Use the time specified in the event instead of the current time
                        // so that downstream code can get more accurate estimates of
                        // event dispatch latency from the time the event is enqueued onto
                        // the evdev client buffer.
                        //
                        // The event's timestamp fortuitously uses the same monotonic clock
                        // time base as the rest of Android.  The kernel event device driver
                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
                        // system call that also queries ktime_get_ts().
                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
 
                        // Bug 7291243: Add a guard in case the kernel generates timestamps
                        // that appear to be far into the future because they were generated
                        // using the wrong clock source.
                        //
                        // This can happen because when the input device is initially opened
                        // it has a default clock source of CLOCK_REALTIME.  Any input events
                        // enqueued right after the device is opened will have timestamps
                        // generated using CLOCK_REALTIME.  We later set the clock source
                        // to CLOCK_MONOTONIC but it is already too late.
                        //
                        // Invalid input event timestamps can result in ANRs, crashes and
                        // and other issues that are hard to track down.  We must not let them
                        // propagate through the system.
                        //
                        // Log a warning so that we notice the problem and recover gracefully.
                        if (event->when >= now + 10 * 1000000000LL) {
                            // Double-check.  Time may have moved on.
                            nsecs_ttime = systemTime(SYSTEM_TIME_MONOTONIC);
                            if (event->when > time) {
                                ALOGW("An input event from %s has a timestamp that appears to "
                                        "have been generated using the wrong clock source "
                                        "(expected CLOCK_MONOTONIC): "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".  "
                                        "Using current time instead.",
                                        device->path.string(), event->when, time, now);
                                event->when = time;
                            } else {
                                ALOGV("Event time is ok but failed the fast path and required "
                                        "an extra call to systemTime: "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".",
                                        event->when, time, now);
                            }
                        }
#else
                        event->when = now;
#endif
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                        device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }
        }
 
        // readNotify() will modify the list of devices so this must be done after
        // processing all other events to ensure that we read all remaining events
        // before closing the devices.
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }
 
        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }
 
        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }
 
        // Poll for events.  Mind the wake lock dance!
        // We hold a wake lock at all times except during epoll_wait().  This works due to some
        // subtle choreography.  When a device driver has pending (unread) events, it acquires
        // a kernel wake lock.  However, once the last pending event has been read, the device
        // driver will release the kernel wake lock.  To prevent the system from going to sleep
        // when this happens, the EventHub holds onto its own user wake lock while the client
        // is processing events.  Thus the system can only sleep if there are no events
        // pending or currently being processed.
        //
        // The timeout is advisory only.  If the device is asleep, it will not wake just to
        // service the timeout.
        mPendingEventIndex = 0;
 
        mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);
 
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
 
        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }
 
        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;
 
            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }
 
    // All done, return the number of events we read.
    return event - buffer;
}

第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件

第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:

struct input_event {
 struct timevaltime;
 __u16type;
 __u16code;
 __s32value;
};

time:事件的执行时间

type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等

code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)

value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值

mNeedToReopenDevices,mClosingDevices一开始初始化为false,跳过:

if (mNeedToReopenDevices) {

while (mClosingDevices) {

mNeedToScanDevices初始化为true,执行到:

if (mNeedToScanDevices) {
    mNeedToScanDevices = false;
    scanDevicesLocked();
    mNeedToSendFinishedDeviceScan = true;
}

mNeedToScanDevices初始化为true,scanDevicesLocked的调用过程:scanDevicesLocked() —-> scanDirLocked(DEVICE_PATH) —-> openDeviceLocked(devname)

DEVICE_PATH被赋值为/dev/input,这是输入设备节点目录,该目录下包含所有输入设备节点,在命令行下进入该目录看看,一般包含:

crw-rw---- root    input    13,  64 1970-01-01 08:00 event0
crw-rw---- root    input    13,  65 1970-01-01 08:00 event1
crw-rw---- root    input    13,  66 1970-01-01 08:00 event2
crw-rw---- root    input    13,  67 1970-01-01 08:00 event3
crw-rw---- root    input    13,  68 1970-01-01 08:00 event4
crw-rw---- root    input    13,  69 1970-01-01 08:00 event5
crw-rw---- root    input    13,  70 1970-01-01 08:00 event6
crw-rw---- root    input    13,  71 1970-01-01 08:00 event7

event0一般代表按键或键盘,可用命令cat /proc/bus/input/device查看所有输入设备详细信息

1.5.3.2    EventHub.cpp的openDeviceLocked函数

int fd = open(devicePath, O_RDWR | O_CLOEXEC);

devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0

open以读写方式打开该设备文件,返回设备文件描述符fd

然后做了一系列动作:获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。

Device* device = new Device(fd, deviceId, String8(devicePath), identifier);

从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象

// See if this is a keyboard.  Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
 || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
 sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
 sizeof_bit_array(BTN_MOUSE))
 || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
 sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
 device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}

检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,表示当前发生输入事件的设备是按键或键盘;

在这段代码之后,系统做了很多事情,比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;如果是键盘、操纵杆等,就配置其布局。这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。

// Register with epoll.
struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = mUsingEpollWakeup ? EPOLLIN : EPOLLIN | EPOLLWAKEUP;
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
 ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
 deletedevice;
 return -1;
}

把设备文件描述符注册到epoll对象中,如果linux内核版本号大于或等于3.5,就设置监控事件为EPOLLIN,表示可读事件,并把设备id保存到data结构体中。

addDeviceLocked(device);
void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

最后调用addDeviceLocked(device)把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices,mOpeningDevices初始化为0,表示下一个设备还没有,同时设mOpeningDevices为当前设备对象。

openDeviceLocked函数执行完毕返回到getEvents的scanDevicesLocked()函数后继续执行

1.5.3.1-2    EventHub.cpp的getEvents函数

mOpeningDevices不为空,进入到while循环:

while (mOpeningDevices != NULL) {

该while循环把所有设备Device的内容保存到RawEvent结构体中,把event->type设为DEVICE_ADDED,意味着事件被成功添加到RawEvent,再把mNeedToSendFinishedDeviceScan设为true,意味着结束设备扫描。继续执行到:

while (mPendingEventIndex < mPendingEventCount) {

mPendingEventCount和mPendingEventIndex都初始化为0,while循环不成立,跳过该while循环

mPendingINotify初始化为false,跳过:

if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {

deviceChanged也为false,跳过:

if (deviceChanged) {

执行到:

if (event != buffer || awoken) {
    break;
}

不相等,break跳出for循环执行到最后event-buffer得到输入设备的个数,返回到loopOnce中执行processEventsLocked —> addDeviceLocked把设备添加到KeyedVector中,同时设置一些变量,比如把keyboardSource为AINPUT_SOURCE_KEYBOARD,表示按键

因为InputReaderThread是循环执行的,进程又一次进入到getEvents函数中,开始执行for循环,这时候设备都已经取出来了就不再执行scanDevicesLocked()扫描获取设备

mPendingEventCount还是为0,所以:

 while (mPendingEventIndex < mPendingEventCount) {

仍然不成立,进程执行到:

mPendingEventIndex = 0;
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

一开始,队列等待中的事件都为0,mPendingEventIndex设为0;调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,InputRead线程处于等待状态,等待被唤醒,什么时候被唤醒

在1.1.3节,提到Q1和Q2两个问题,Q1在此处可以解答。

要想回答Q2,就必须要清楚epoll兴趣列表中注册了哪些文件描述符,在1.1.3节可知,注册了mINotifyFd, mWakeReadPipeFd和mWakeWritePipeFd;由1.5.3.2可知,注册了设备文件描述符fd。要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。搜索一下代码,发现EventHub.cpp的wake()方法中有管道写入端描述符mWakeWritePipeFd,这是一个线索:

1.5.3.3    EventHub.cpp的wake()

void EventHub::wake() {
    ALOGV("wake() called");
 
    ssize_tnWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);
 
    if (nWrite != 1 && errno != EAGAIN) {
        ALOGW("Could not write wake signal, errno=%d", errno);
    }
}

在wake方法中,管道写入端mWakeWritePipeFd写入了一个字符w,管道中有数据了,epoll机制监控到管道有I/O事件发生,于是epoll_wait执行,返回发生I/O事件的个数,并且把发生的事件放在epoll_event结构数组mPendingEventItems中,这时调用者线程InputReaderThread被唤醒,这就回答了1.1.3节Q2问题,但是新的问题又来了

Q7:wake方法是什么被调用的?被谁调用的?

先继续epoll_wait方法后面的代码分析

1.5.3.1-3    EventHub.cpp的getEvents函数

epoll_wait返回发生事件的个数赋给pollResult,pollResult肯定大于0,执行:

mPendingEventCount = size_t(pollResult);

pollResult赋给了mPendingEventCount,表明有事件待处理

再次执行getEvents中的for循环

mOpeningDevices在1.5.3.1-1节中已经被赋为device->next即0,所以while循环:

while (mClosingDevices) {

不成立,跳过。

mPendingEventCount大于0了,mPendingEventIndex < mPendingEventCount成立,进入到下面while循环:

while (mPendingEventIndex < mPendingEventCount) {

根据发生事件的类型,判断:

if (eventItem.data.u32 == EPOLL_ID_WAKE) {

成立,进程执行到:

do {
    nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));

从管道中读取数据,该数据就是一个字符w,执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;然后1.5.3.3节的wake()函数又调用write写入字符w触发了epoll_wait,唤醒了InputReader线程,然后又开始执行for循环……

Q8    这样循环下去的意义何在?

循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句:

if (pollResult == 0) {
    // Timed out.
    mPendingEventCount = 0;
    break;
}

成立,跳出for循环返回了,如果此时有事件发生的话,因为epoll_wait还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。

1.5.3.1-4    EventHub.cpp的getEvents函数

在1.5.3.2节openDeviceLocked函数中,把输入设备文件描述符注册到mEpollFd中,因此,当有输入事件发生时,epoll_wait方法开始执行返回发生的个数赋给pollResult变量,此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环,进入到:

while (mPendingEventIndex < mPendingEventCount) {

此时eventItem.data.u32的值是设备id,if语句:

if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {

if (eventItem.data.u32 == EPOLL_ID_WAKE) {

都不成立,进程执行到:

Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
    int32_treadSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);

根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中,如果没出错的话,执行:

int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_tcount = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
    struct input_event& iev = readBuffer[i];

计算出有多少个事件并取出来保存到input_event结构体变量iev中,然后计算相应的事件执行时间,再把input_event结构体变量中的内容保存到原始事件RawEvent中,然后循环直到所有事件都被获取到后,进程执行:

// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
    break;
}

event指向的内存现在已经保存了事件,而buffer还是指向首地址,不相等,if成立,执行break跳出for(::)无限循环,执行到getEvents最后一句:

return event - buffer;

返回读取事件的个数,并返回到InputReader的loopOnce中这一句:

size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

count大于0,执行:

if (count) {
    processEventsLocked(mEventBuffer, count);
}

调用processEventsLocked对事件进行处理,此处属于事件处理阶段,在第2节分析。

第1节做了3件事:a. 创建InputDispatchThread, InputReaderThread,InputManager等主要输入系统核心组件

b. 扫描所有输入设备,获取输入设备各种信息

c. 获取输入设备事件

第1节Framework层如何获取事件的过程分析完毕,还留下Q6问题

2.   Framework层获得按键事件后如何处理、存储

通过getEvents获得事件后,执行过程:processEventsLocked —-> processEventsForDeviceLocked

在processEventsLocked中,rawEvent->type表示事件的类型,有如下几种:

EV_KEY,EV_ABS,EV_REL,EV_SW,EV_LED,EV_FF,...

本文针对按键事件,因此type是EV_KEY,小于FIRST_SYNTHETIC_EVENT,

2.1    InputReader.cpp的processEventsForDeviceLocked函数

void InputReader::processEventsForDeviceLocked(int32_tdeviceId,
 const RawEvent* rawEvents, size_tcount) {
ssize_tdeviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
    ALOGW("Discarding event for unknown deviceId %d.", deviceId);
    return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
    //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
    return;
}
    device->process(rawEvents, count);
}

从KeyedVector容器中根据设备id号取出设备对象赋给device,参数count为多少?正常情况下按键按下一次,产生一个按下事件,类型为EV_KEY,还有一个同步事件EV_SYNC,因此count为2

然后调用过程为:

device->process(rawEvents, count) —-> mapper->process(rawEvent) —-> KeyboardInputMapper::processKey

不同的输入时间对应不同的InputMapper对象,按键事件对应的是KeyboardInputMapper对象,就执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。

再根据按键类型,执行到KeyboardInputMapper::processKey函数,在processKey中,主要代码在最后部分:

 int32_toldMetaState = mMetaState;
    int32_tnewMetaState = updateMetaState(keyCode, down, oldMetaState);
    bool metaStateChanged = oldMetaState != newMetaState;
    if (metaStateChanged) {
        mMetaState = newMetaState;
        updateLedState(false);
    }
 
    nsecs_tdownTime = mDownTime;
 
    // Key down on external an keyboard should wake the device.
    // We don't do this for internal keyboards to prevent them from waking up in your pocket.
    // For internal keyboards, the key layout file should specify the policy flags for
    // each wake key individually.
    // TODO: Use the input device configuration to control this behavior more finely.
    if (down && getDevice()->isExternal()) {
        policyFlags |= POLICY_FLAG_WAKE;
    }
 
    if (mParameters.handlesKeyRepeat) {
        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
    }
 
    if (metaStateChanged) {
        getContext()->updateGlobalMetaState();
    }
 
    if (down && !isMetaKey(keyCode)) {
        getContext()->fadePointer();
    }
 
    NotifyKeyArgsargs(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);

NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:

nsecs_t eventTime:按键事件的发生时间

int32_t deviceId:按键的设备id

uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘

uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。

int32_t action:按键动作标志,是按下按键还是松开按键

int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM

int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置

int32_t scanCode:原始按键值,就是从底层获取到的按键值

int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0

nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量

getListener()->notifyKey(&args);
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

mQueuedListener是在1.1.6节创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数,如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。

2.2    InputDispatcher.cpp的notifyKey函数

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
    ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
            args->eventTime, args->deviceId, args->source, args->policyFlags,
            args->action, args->flags, args->keyCode, args->scanCode,
            args->metaState, args->downTime);
#endif
    if (!validateKeyEvent(args->action)) {
        return;
    }
 
    uint32_tpolicyFlags = args->policyFlags;
    int32_tflags = args->flags;
    int32_tmetaState = args->metaState;
    if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
        policyFlags |= POLICY_FLAG_VIRTUAL;
        flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
    }
    if (policyFlags & POLICY_FLAG_FUNCTION) {
        metaState |= AMETA_FUNCTION_ON;
    }
 
    policyFlags |= POLICY_FLAG_TRUSTED;
 
    int32_tkeyCode = args->keyCode;
    if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
        int32_tnewKeyCode = AKEYCODE_UNKNOWN;
        if (keyCode == AKEYCODE_DEL) {
            newKeyCode = AKEYCODE_BACK;
        } else if (keyCode == AKEYCODE_ENTER) {
            newKeyCode = AKEYCODE_HOME;
        }
        if (newKeyCode != AKEYCODE_UNKNOWN) {
            AutoMutex_l(mLock);
            struct KeyReplacementreplacement = {keyCode, args->deviceId};
            mReplacedKeys.add(replacement, newKeyCode);
            keyCode = newKeyCode;
            metaState &= ~AMETA_META_ON;
        }
    } else if (args->action == AKEY_EVENT_ACTION_UP) {
        // In order to maintain a consistent stream of up and down events, check to see if the key
        // going up is one we've replaced in a down event and haven't yet replaced in an up event,
        // even if the modifier was released between the down and the up events.
        AutoMutex_l(mLock);
        struct KeyReplacementreplacement = {keyCode, args->deviceId};
        ssize_tindex = mReplacedKeys.indexOfKey(replacement);
        if (index >= 0) {
            keyCode = mReplacedKeys.valueAt(index);
            mReplacedKeys.removeItemsAt(index);
            metaState &= ~AMETA_META_ON;
        }
    }
 
    KeyEventevent;
    event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);
 
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
 
    bool needWake;
    { // acquire lock
        mLock.lock();
 
        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();
 
            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }
 
            mLock.lock();
        }
 
        int32_trepeatCount = 0;
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);
 
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock
 
    if (needWake) {
        mLooper->wake();
    }
}

该函数做了很重要的任务:前半部分对传递过来的按键事件进行检查、验证,之后处理特殊按键,再把按键放到InboundQueue队列中,最后调用Looper对象的wake像管道中写入字符唤醒InputDispatcherThread线程

if (!validateKeyEvent(args->action)) {
    return;
}

检查按键是否按下、松开

if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
    policyFlags |= POLICY_FLAG_VIRTUAL;
    flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}

设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件,比如手机上除了屏幕显示外的MENU、HOME、BACK键

if (policyFlags & POLICY_FLAG_FUNCTION) {
    metaState |= AMETA_FUNCTION_ON;
}

设置metaState为meta键标志

policyFlags |= POLICY_FLAG_TRUSTED;

设置该标志表示按键事件是可信任的,比如,一个直接与Android机器相连接的按键设备

KeyEventevent;
event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime);

把参数传递给KeyEvent对象event

mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等,这一步将在第7节分析

needWake = enqueueInboundEventLocked(newEntry);

创建KeyEntry对象并把按键事件作为该对象的内容,enqueueInboundEventLocked函数根据输入事件的类型作进一步处理:

bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
 
switch (entry->type) {
case EventEntry::TYPE_KEY: {
 // Optimize app switch latency.
 // If the application takes too long to catch up then we drop all events preceding
 // the app switch key.
 KeyEntry* keyEntry = static_cast(entry);
 if (isAppSwitchKeyEventLocked(keyEntry)) {
 if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
 mAppSwitchSawKeyDown = true;
 } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
 if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
 ALOGD("App switch is pending!");
#endif
 mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
 mAppSwitchSawKeyDown = false;
 needWake = true;
 }
 }
 }
 break;
}

mInboundQueue一开始没有数据为空,enqueueAtTail函数把按键事件对象KeyEntry插入到mInboundQueue队尾。

case语句用来切换app,如果是HOME、挂号按键,就把needWake设为true,表示可以唤醒InputDispatcherThread来分发按键事件,否则,不唤醒InputDispatcherThread,那么按键就不会处理

if (needWake) {
    mLooper->wake();
}

wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程是谁?在1.5.2.2节,InputDispatcherThread线程执行到了Looper对象的pollOnce函数时处于阻塞状态,此时epoll机制检测到管道中有数据写入事件发生,由这篇文章:Handler机制可知,读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread,Q6也就有了答案。

小结:

把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。

现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。

3    Framework层如何分发、传递给应用层

由2.2节可知,InputDispatcherThread线程被唤醒后,又一次地执行dispatchOnce函数,在dispatchOnce中调用了dispatchOnceInnerLocked函数

3.1    InputDispatcher.cpp的dispatchOnceInnerLocked

mInboundQueue中已经有了按键事件,不为空,执行到这句:

else {
    // Inbound queue has at least one entry.
    mPendingEvent = mInboundQueue.dequeueAtHead();
}

dequeueAtHead函数从队列mInboundQueue头部取出一个元素EventEntry对象交给mPendingEvent,EventEntry代表按键事件

在switch语句中,根据类型来选择执行语句,按键为TYPE_KEY

case EventEntry::TYPE_KEY: {
 KeyEntry* typedEntry = static_cast(mPendingEvent);
      ......
 done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
 break;
}

忽略不重要的代码,下一步调用dispatchKeyLocked函数:

if (! entry->dispatchInProgress) {
 if (entry->repeatCount == 0
 && entry->action == AKEY_EVENT_ACTION_DOWN
 && (entry->policyFlags & POLICY_FLAG_TRUSTED)
 && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
 if (mKeyRepeatState.lastKeyEntry
 && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
 // We have seen two identical key downs in a row which indicates that the device
 // driver is automatically generating key repeats itself.  We take note of the
 // repeat here, but we disable our own next key repeat timer since it is clear that
 // we will not need to synthesize key repeats ourselves.
 entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
 resetKeyRepeatLocked();
 mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
 } else {
 // Not a repeat.  Save key down state in case we do see a repeat later.
 resetKeyRepeatLocked();
 mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
 }
 mKeyRepeatState.lastKeyEntry = entry;
 entry->refCount += 1;
 } else if (! entry->syntheticRepeat) {
 resetKeyRepeatLocked();
 }
 
 if (entry->repeatCount == 1) {
 entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
 } else {
 entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
 }
 
 entry->dispatchInProgress = true;
 
 logOutboundKeyDetailsLocked("dispatchKey - ", entry);
}

先判断是否包含重复按键事件,如果是,暂时先清除掉,然后计算repeatCount值;如果没有重复按键事件,就保存按键状态、执行时间;如果计算后的repeatCount为1,设flags为AKEY_EVENT_FLAG_LONG_PRESS,表示长按事件。

entry->dispatchInProgress = true;

把dispatchInProgress设为true,意味着即将进行分发事件了

if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
 if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
 CommandEntry* commandEntry = postCommandLocked(
 & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
 if (mFocusedWindowHandle != NULL) {
 commandEntry->inputWindowHandle = mFocusedWindowHandle;
 }
 commandEntry->keyEntry = entry;
 entry->refCount += 1;
 return false; // wait for the command to run
 } else {
 entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
 }
}

entry->interceptKeyResult一开始默认为INTERCEPT_KEY_RESULT_UNKNOWN,if语句成立

如果entry->policyFlags为POLICY_FLAG_PASS_TO_USER,表示按键需要传递给应用程序;doInterceptKeyBeforeDispatchingLockedInterruptible函数也是拦截按键处理的方法,在7.2节详解

// Identify targets.
Vector inputTargets;
int32_tinjectionResult = findFocusedWindowTargetsLocked(currentTime,
        entry, inputTargets, nextWakeupTime);
setInjectionResultLocked(entry, injectionResult);

InputTarget结构体:指定一个输入事件如何被分发到一个特定窗口。该结构体包含了很多特征变量:x,y坐标,输入事件通道InputChannel等;findFocusedWindowTargetsLocked找到获得焦点的窗口,如果成功,返回值设为INPUT_EVENT_INJECTION_SUCCEEDED,并把获得焦点的窗口的输入通道保存到InputTarget对象中,再把InputTarget对象放到inputTargets堆栈顶,setInjectionResultLocked函数把该获得焦点的标志INPUT_EVENT_INJECTION_SUCCEEDED保存到InjectionState结构体元素injectionResult中

// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);

dispatchEventLocked函数从inputTarget容器中取出获得焦点的输入通道管理对象Connection,Connection用来管理单个输入通道InputChannel的分发状态。dispatchEventLocked函数的调用过程:

dispatchEventLocked —-> prepareDispatchCycleLocked —-> enqueueDispatchEntriesLocked

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_tcurrentTime,
 const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
 
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
 startDispatchCycleLocked(currentTime, connection);
}
}

outboundQueue一开始为空,当执行完enqueueDispatchEntryLocked后,outboundQueue就有了按键事件DispatchEntry,不为空,所以if语句:

if (wasEmpty && !connection->outboundQueue.isEmpty()) {

成立,startDispatchCycleLocked开始发送事件,先看enqueueDispatchEntryLocked:

enqueueDispatchEntryLocked函数相关代码:

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_tdispatchMode) {
    int32_tinputTargetFlags = inputTarget->flags;
    if (!(inputTargetFlags & dispatchMode)) {
        return;
    }
    inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
 
    // This is a new event.
    // Enqueue a new dispatch entry onto the outbound queue for this connection.
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->scaleFactor);
 
    // Apply target flags and update the connection's input state.
    switch (eventEntry->type) {
    case EventEntry::TYPE_KEY: {
        KeyEntry* keyEntry = static_cast(eventEntry);
        dispatchEntry->resolvedAction = keyEntry->action;
        dispatchEntry->resolvedFlags = keyEntry->flags;
 
        if (!connection->inputState.trackKey(keyEntry,
                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
            ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
                    connection->getInputChannelName());
#endif
            deletedispatchEntry;
            return; // skip the inconsistent event
        }
        break;
    }
 
  ......
    }
 
    // Remember that we are waiting for this dispatch to complete.
    if (dispatchEntry->hasForegroundTarget()) {
        incrementPendingForegroundDispatchesLocked(eventEntry);
    }
 
    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
}

最后一个参数dispatchMode针对触摸的,与触摸相关的代码暂时去掉

DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->scaleFactor);

创建DispatchEntry按键事件对象,把该对象作为一个发送数据包加入到outboundQueue队列中

trackKey函数把按键分为按下和松开两类事件处理,如果是按下就就先保存到KeyMemento结构体中,如果是在松开状态下,该事件是回退事件,即不在应用程序中处理的事件,就从回退事件集合中删除该按键

// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);

再把新构建的按键事件数据包添加到outboundQueue队列尾部即栈顶。

startDispatchCycleLocked函数:

while (connection->status == Connection::STATUS_NORMAL
 && !connection->outboundQueue.isEmpty()) {
    DispatchEntry* dispatchEntry = connection->outboundQueue.head;
    dispatchEntry->deliveryTime = currentTime;
 
    // Publish the event.
    status_tstatus;
    EventEntry* eventEntry = dispatchEntry->eventEntry;
    switch (eventEntry->type) {
    case EventEntry::TYPE_KEY: {
 KeyEntry* keyEntry = static_cast(eventEntry);
 
 // Publish the key event.
 status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
 keyEntry->deviceId, keyEntry->source,
 dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
 keyEntry->keyCode, keyEntry->scanCode,
 keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
 keyEntry->eventTime);
 break;
    }
}

当outboundQueue非空并且其他条件都具备时(比如,inputchannel已经注册,通信没有错误等),取出outboundQueue队列头元素,赋给dispatchEntry,再取出事件对象KeyEntry,根据事件类型确定case语句分支,如果是按键事件,就调用connection的InputPublisher的publishKeyEvent函数发送到inputchannel中,如果publishKeyEvent返回0,表示按键事件发送成功

3.2    InputTransport.cpp的publishKeyEvent

status_tInputPublisher::publishKeyEvent(
        uint32_tseq,
        int32_tdeviceId,
        int32_tsource,
        int32_taction,
        int32_tflags,
        int32_tkeyCode,
        int32_tscanCode,
        int32_tmetaState,
        int32_trepeatCount,
        nsecs_tdownTime,
        nsecs_teventTime) {
#if DEBUG_TRANSPORT_ACTIONS
    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
            "downTime=%lld, eventTime=%lld",
            mChannel->getName().string(), seq,
            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
            downTime, eventTime);
#endif
 
    if (!seq) {
        ALOGE("Attempted to publish a key event with sequence number 0.");
        return BAD_VALUE;
    }
 
    InputMessagemsg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

创建Message消息,把传递过来的事件信息保存到Message对象中,调用服务端管道InputChannel的sendMessage方法发送消息,sendMessage源码:

status_tInputChannel::sendMessage(const InputMessage* msg) {
    size_tmsgLength = msg->size();
    ssize_tnWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
 
    if (nWrite < 0) {
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
                msg->header.type, error);
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }
 
    if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
                mName.string(), msg->header.type);
#endif
        return DEAD_OBJECT;
    }
 
#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endif
    return OK;
}

send系统调用相当于write调用,区别在于:send专用于socket文件描述符;send多了一个flags参数表示I/O事件的行为。send向服务端fd发送消息msg,如果成功,返回成功发送消息的大小;返回-1表示发送了错误

在3.4.1.1节通过socketpair创建一对socket对象,一个用作服务端,一个用作客户端,并且是已经连接好的插口,无需再通过bind、connect去连接,两端都可以发送、接收,全双工通信。在按键事件中,用socket[0]作为服务端,通过send向服务端发送数据,recv从客户端socket[1]接收数据。

在3.5.3.1节,socket客户端文件描述符被注册到了主线程epoll兴趣列表中,如果socket服务端没有往inputchannel中写入数据,客户端暂时处于阻塞状态,一旦服务端有了数据后,客户端也被唤醒了,客户端唤醒后的处理过程在第5节分析。

Q9    在publishKeyEvent中调用这句时:

return mChannel->sendMessage(&msg)

mChannel是服务端管道对象,该对象如何创建、注册的?socket文件描述符mFd什么时候创建;既然有服务端,肯定有客户端来接收数据,那么应用程序客户端socket如何创建?应用程序客户端InputChannel对象又是什么时候创建、注册的?

关于此问题将在第4节分析

4    Framework层服务端管道和应用程序客户端管道如何创建、注册

涉及到应用程序相关的,就从ViewRootImpl来分析

4.1    ViewRootImpl.java的setView方法

if ((mWindowAttributes.inputFeatures
 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
    mInputChannel = new InputChannel();
}

当窗口没有按键事件传输通道时,创建一个InputChannel对象,相当于一个管道,其他进程通过该管道像窗口发送按键事件消息。

res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), 
mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);

mWindowSession在ViewRootImpl构造器中创建:

mWindowSession = WindowManagerGlobal.getWindowSession();

4.2    WindowManagerGlobal.java的getWindowSession方法

public static IWindowSessiongetWindowSession() {
    synchronized (WindowManagerGlobal.class) {
    if (sWindowSession == null) {
        try {
            InputMethodManagerimm = InputMethodManager.getInstance();
            IWindowManagerwindowManager = getWindowManagerService();
            sWindowSession = windowManager.openSession(
            new IWindowSessionCallback.Stub() {
                @Override
                public void onAnimatorScaleChanged(float scale) {
                    ValueAnimator.setDurationScale(scale);
                }
            },
            imm.getClient(), imm.getInputContext());
        } catch (RemoteException e) {
 Log.e(TAG, "Failed to open window session", e);
 }
 }
 return sWindowSession;
 }
}

getWindowManagerService方法源码:

public static IWindowManagergetWindowManagerService() {
    synchronized (WindowManagerGlobal.class) {
    if (sWindowManagerService == null) {
        sWindowManagerService = IWindowManager.Stub.asInterface(
 ServiceManager.getService("window"));
 try {
    sWindowManagerService = getWindowManagerService();
    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
 } catch (RemoteException e) {
    Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
 }
    }
    return sWindowManagerService;
    }
}

getWindowSession方法做了很多工作,单例类InputMethodManager通过getInstance返回其唯一的对象,InputMethodManager表示输入法框架系统级API,用以输入法和应用程序之间的交互。

getService(“window”)获得一个WindowManagerService服务的Binder对象,实际是BinderProxy对象,asInterface接口根据BinderProxy对象生成Proxy客户端代理对象,返回到getWindowSession中赋给IWindowManager接口类型windowManager变量,然后利用该代理对象来访问WindowManagerService服务端中的方法openSession,其参数涵义:

第一个参数是一个IWindowSessionCallback.Stub对象

第二个参数imm.getClient(),返回IInputMethodClient.Stub对象

第三个参数imm.getInputContext(),返回ControlledInputConnectionWrapper对象

在openSession方法中,创建了Session对象,并把这三个参数及WindowManagerService对象传递到Session中

得到Session对象后,返回到4.1节ViewRootImpl构造器中赋给IWindowSession接口类型mWindowSession,再调用Session对象的addToDisplay

4.3    Session.java的addToDisplay方法

public int addToDisplay(IWindowwindow, int seq, WindowManager.LayoutParamsattrs,
 int viewVisibility, int displayId, RectoutContentInsets, RectoutStableInsets,InputChanneloutInputChannel) {
 return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
 outContentInsets, outStableInsets, outInputChannel);
}

mWindow:W对象,W实现了IWindow.Stub抽象类,继承了Binder类

mSeq:一个整型,每个按键事件的序号

mWindowAttributes:初始化语句有:

final WindowManager.LayoutParamsmWindowAttributes = new WindowManager.LayoutParams();

创建了一个布局参数对象

mAppVisible:确定mView即DecorView的可见性,默认为true,可见

mDisplay.getDisplayId():获得应用程序逻辑显示区域id号

mAttachInfo.mContentInsets:

final RectmContentInsets = new Rect();

一个矩形对象,作为窗口可见的矩形修饰

mAttachInfo.mStableInsets:

final RectmStableInsets = new Rect();

也是一个矩形对象,作为窗口固定的修饰

mInputChannel:待注册的管道对象

mService是WindowManagerService对象,在addToDisplay中调用WindowManagerService的addWindow方法

4.4    WindowManagerService.java的addWindow方法

与inputChannel相关部分:

if (outInputChannel != null && (attrs.inputFeatures
        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
    String name = win.makeInputChannelName();
    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
    win.setInputChannel(inputChannels[0]);
    inputChannels[1].transferTo(outInputChannel);
    mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}

4.4.1    InputChannel的openInputChannelPair

openInputChannelPair的实现在本地方法android_view_InputChannel_nativeOpenInputChannelPair中:

static jobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclassclazz, jstringnameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);
 
    sp serverChannel;
    sp clientChannel;
    status_tresult = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
 
    if (result) {
        String8message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }
 
    jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }
 
    jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }
 
    jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }
 
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

声明服务端管道serverChannel、客户端管道clientChannel,作为openInputChannelPair的参数

4.4.1.1    InputTransprot.cpp的openInputChannelPair

status_tInputChannel::openInputChannelPair(const String8& name,
        sp& outServerChannel, sp& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_tresult = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }
 
    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
 
    String8serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
 
    String8clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}
int sockets[2];
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)

创建一个饱含2个元素的socket数组,socketpair创建一对socket对象,SOCK_SEQPACKET表示创建连续可靠的数据包连接,如果创建成功,返回0,如果返回-1,出错。

outServerChannel = new InputChannel(serverChannelName, sockets[0]);
outClientChannel = new InputChannel(clientChannelName, sockets[1]);

以这一对socket文件描述符、通道名称作为参数分别创建服务端管道对象outServerChannel和客户端管道对象clientChannelName

在InputChannel构造函数中,把管道名称传给mName,socket文件描述符保存到mFd中,设置mFd为非阻塞模式

4.4.1.2    android_view_InputChannel.cpp的android_view_InputChannel_nativeOpenInputChannelPair

jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);

创建包含2个元素的数组,数组对象类型是gInputChannelClassInfo.clazz即InputChannel类型,元素初始化为空

jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));

用已创建的服务端通道serverChannel和客户端通道clientChannel作为参数分别创建本地通道对象NativeInputChannel,android_view_InputChannel_createInputChannel:

static jobjectandroid_view_InputChannel_createInputChannel(JNIEnv* env,
        NativeInputChannel* nativeInputChannel) {
    jobjectinputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);
    if (inputChannelObj) {
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    }
    return inputChannelObj;
}

创建Java层InputChannel类的对象保存到inputChannelObj中,如果成功,再调用android_view_InputChannel_setNativeInputChannel把刚才创建的NativeInputChannel对象指针赋给InputChannel对象中的变量Ptr,最后返回新创建的java层的InputChannel对象分别赋给serverChannelObj和clientChannelObj

env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;

用新创建的一对管道对象初始化数组channelPair,返回该数组赋给WindowManagerService中的inputChannels数组

小结:

makeInputChannelName创建了唯一的管道名称,openInputChannelPair以该名称作为参数创建一对管道InputChannel对象,存储在数组inputChannels中,inputChannels[0]作为服务端管道提供给inputdispatcher使用,用来发送按键事件;inputChannels[1]作为客户端管道提供给应用程序主线程使用,用来接收、消费按键事件。

4.4.2    InputManagerService的registerInputChannel

服务端管道inputChannels[0]创建好后,需要注册:

mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);

参数win.mInputWindowHandle是一个输入事件窗口的句柄,有多个作用:InputDispatcher在发送按键事件前会通过该句柄检查是否有获得焦点的窗口;如果有焦点的窗口,可以通过该句柄获得应用程序的InputChannel

registerInputChannel —-> nativeRegisterInputChannel

4.4.2.1    com_android_server_input_InputManagerService.cpp的nativeRegisterInputChannel函数

static void nativeRegisterInputChannel(JNIEnv* env, jclassclazz,
        jlongptr, jobjectinputChannelObj, jobjectinputWindowHandleObj, jbooleanmonitor) {
    NativeInputManager* im = reinterpret_cast(ptr);
 
    sp inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }
 
    sp inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
 
    status_tstatus = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        String8message;
        message.appendFormat("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return;
    }
 
    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}

先获得本地InputChannel对象和InputWindowHandle对象,再调用NativeInputManager的registerInputChannel函数:

status_tNativeInputManager::registerInputChannel(JNIEnv* env,
        const sp& inputChannel,
        const sp& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}

通过InputManager对象获得对应的InputDispatcher对象,进而调用其registerInputChannel函数注册

status_tInputDispatcher::registerInputChannel(const sp& inputChannel,
        const sp& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
            toString(monitor));
#endif
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }
 
        sp connection = new Connection(inputChannel, inputWindowHandle, monitor);
 
        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);
 
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
 
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock
 
    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}
if (getConnectionIndexLocked(inputChannel) >= 0) {
    ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
    return BAD_VALUE;
}

检查inputChannel是否注册,首次执行时,没有注册

sp connection = new Connection(inputChannel, inputWindowHandle, monitor);

通过创建的管道inputChannel以及窗口句柄inputWindowHandle创建Connection对象,Connection用来管理inputChannel

int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);

getFd获得服务端管道inputChannel上的socket文件描述符,然后以键值对的方式把connect加入到KeyedVector中,这就完成了服务端通道的注册。

mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

然后把socket服务端加入到epoll兴趣列表中,mLooper属于InputDispatcherThread,不属于主线程,注意与4.5.3.1节的区别,4.5.3.1节是注册到主线程对应的epoll对象中。ALOOPER_EVENT_INPUT表示可读事件,意思是在服务端socket中读取数据,这样设置的目的在于,当客户端socket接受到事件处理完后会向服务端发送一个反馈信号,服务端接收此信号后会回调handleReceiveCallback函数,该函数会再一次执行startDispatchCycleLocked函数从outboundQueue中取出按键事件进行发送,循环处理。

addFd函数的具体执行过程与4.5.4节几乎一样,不再分析。

4.4.3    InputChannel.java的transferTo

在4.4.2节已经注册了服务端管道inputChannels[0],那么客户端管道inputChannels[1]如何注册?

inputChannels[1].transferTo(outInputChannel);
public void transferTo(InputChanneloutParameter) {
    if (outParameter == null) {
 throw new IllegalArgumentException("outParameter must not be null");
    } 
    nativeTransferTo(outParameter);
}

outInputChannel是InputChannel对象,inputChannels[1]也是刚刚创建的InputChannel对象,transferTo两头都是InputChannel对象,这又是怎么回事

transferTo的调用者是当前新创建客户端管道对象,参数outInputChannel是在ViewRootImpl中创建的,两者是不同的对象。

4.4.3.1    nativeTransferTo的本地实现

static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobjectobj,
        jobjectotherObj) {
    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Other object already has a native input channel.");
        return;
    }
 
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}

env:JNI接口对象

obj:本地方法所在对象的引用,就是inputChannels[1]客户端对象的引用

otherObj:在ViewRootImpl中创建的InputChannel对象

首先调用android_view_InputChannel_getNativeInputChannel函数

static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobjectinputChannelObj) {
    jlonglongPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast(longPtr);
}

传过来的对象时otherObj,其变量mPtr在初始化时为空,没有额外赋值,所以longPtr为空,这句:

if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {

不成立

又一次调用android_view_InputChannel_getNativeInputChannel函数,只不过第二个参数是obj即inputChannels[1],此时inputChannels[1]中的mPtr指针不为空,因此在3.4.1.2节中设置了mPtr为inputChannels[1]对应的NativeInputChannel地址,返回NativeInputChannel指针给nativeInputChannel变量,而后调用android_view_InputChannel_setNativeInputChannel函数把otherObj中的mPtr设为NativeInputChannel对象地址,这就表明ViewRootImpl中创建的InputChannel对象是客户端管道

读到此处可知,transferTo方法只是把ViewRootImpl中创建的InputChannel对象中的mPtr变量赋了值,该值就是客户端InputChannle对应的NativeInputChannel对象地址。

4.5    ViewRootImpl.java的setView方法

在4.4.2.1节,注册服务端管道InputChannel[0]时,创建了InputPublisher对象,用来发送消息到input channel,那么注册在客户端管道InputChannel[1]时,也会创建一个接收消息的对象,这就是InputConsumer,从管道中获取事件。客户端如何创建?还是在ViewRootImpl.java的setView方法中:

if (viewinstanceof RootViewSurfaceTaker) {
 mInputQueueCallback =
 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
 if (mInputQueueCallback != null) {
 mInputQueue = new InputQueue();
 mInputQueueCallback.onInputQueueCreated(mInputQueue);
 }
 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
 Looper.myLooper());
}

view是DecorView对象,DecorView实现了RootViewSurfaceTaker接口,因此,view也是RootViewSurfaceTaker接口类型,if语句成立,willYouTakeTheInputQueue实际返回一个NativeActivity对象,因为NativeActivity实现了InputQueue.Callback接口

InputQueue:应用程序通过InputQueue来获得输入设备事件

InputQueue.Callback:当InputQueue绑定、解除绑定与线程关系时的回调接口

创建了InputQueue对象,作为NativeActivity中回调方法onInputQueueCreated的参数;用InputChannel和Looper对象作为参数创建WindowInputEventReceiver对象并用mInputEventReceiver指向该对象。

InputQueue构造器中调用了本地方法nativeInit

mPtr = nativeInit(new WeakReference(this), Looper.myQueue());

第一个参数是InputQueue弱引用对象,第二个参数是主线程MessageQueue对象

4.5.1    android_view_InputQueue.cpp的nativeInit

static jlongnativeInit(JNIEnv* env, jobjectclazz, jobjectqueueWeak, jobjectjMsgQueue) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    sp queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
    if (!queue.get()) {
        jniThrowRuntimeException(env, "InputQueue failed to initialize");
        return 0;
    }
    queue->incStrong(&gInputQueueClassInfo);
    return reinterpret_cast(queue.get());
}

android_os_MessageQueue_getMessageQueue获得C++层NativeMessageQueue对象,也是MessageQueue对象,getLooper()获得Looper对象,queueWeak是java层传递过来的InputQueue弱引用对象,createQueue源码:

InputQueue* InputQueue::createQueue(jobjectinputQueueObj, const sp& looper) {
    int pipeFds[2];
    if (pipe(pipeFds)) {
        ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
        return NULL;
    }
    fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
    fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
    return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
}

创建了一个管道pipeFds,设置管道读、写端为非阻塞模式

创建了c++层InputQueue对象,赋给queue变量。InputQueue的构造函数:

InputQueue::InputQueue(jobjectinputQueueObj, const sp& looper,
        int dispatchReadFd, int dispatchWriteFd) :
        mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
        mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
}

分别把管道读端、写端文件描述符赋给mDispatchReadFd、mDispatchWriteFd;用JNI接口方法NewGlobalRef创建inputQueueObj本地全局引用赋给mInputQueueWeakGlobal

4.5.2    NativeActivity的onInputQueueCreated

public void onInputQueueCreated(InputQueuequeue) {
 if (!mDestroyed) {
 mCurInputQueue = queue;
 onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr());
 }
}

把创建的InputQueue对象赋给mCurInputQueue,当创建好InputQueue后,既可回调onInputQueueCreated重写自己的代码,表明可以从InputQueue中获取事件

4.5.3    WindowInputEventReceiver的构造器

mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
public WindowInputEventReceiver(InputChannelinputChannel, Looperlooper) {
    super(inputChannel, looper);
}
public InputEventReceiver(InputChannelinputChannel, Looperlooper) {
 if (inputChannel == null) {
 throw new IllegalArgumentException("inputChannel must not be null");
 }
 if (looper == null) {
 throw new IllegalArgumentException("looper must not be null");
 }
 
 mInputChannel = inputChannel;
 mMessageQueue = looper.getQueue();
 mReceiverPtr = nativeInit(new WeakReference(this),
 inputChannel, mMessageQueue);
 
 mCloseGuard.open("dispose");
}

调用WindowInputEventReceiver的构造方法创建了一个WindowInputEventReceiver对象,其构造方法没有实现,直接调用父类InputEventReceiver的构造方法,第一个参数就是客户端管道对象,其内部mPtr变量指向了客户端NativeInputChannel对象地址。

mReceiverPtr = nativeInit(new WeakReference(this),
                inputChannel, mMessageQueue);

第一个参数this,实际类型是WindowInputEventReceiver类型,这个参数很重要,在5.3节会根据该类型决定调用哪个onInputEvent方法。

static jlongnativeInit(JNIEnv* env, jclassclazz, jobjectreceiverWeak,
        jobjectinputChannelObj, jobjectmessageQueueObj) {
    sp inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }
 
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    sp receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_tstatus = receiver->initialize();
    if (status) {
        String8message;
        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }
 
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast(receiver.get());
}
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobjectreceiverWeak, const sp& inputChannel,
        const sp& messageQueue) :
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
        mBatchedInputEventPending(false), mFdEvents(0) {
}

用本地InputChannel, NativeMessageQueue及java层InputEventReceiver对象作为参数创建本地NativeInputEventReceiver对象并初始化,receiverWeak就是传递过来的WindowInputEventReceiver对象,赋给了mReceiverWeakGlobal变量。

创建好NativeInputEventReceiver对象后返回到java层InputEventReceiver中赋给mReceiverPtr变量,java层拥有这个变量就等于拥有了c++层NativeInputEventReceiver对象的指针

4.5.3.1    NativeInputEventReceiver的initialize函数

status_tNativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

InputConsumer获取客户端inputChannel管道上的socket文件描述符添加到epoll对象兴趣列表中,fd是socket的客户端。mMessageQueue是主线程中的消息队列,getLooper返回主线程Looper对象,因此,客户端socket文件描述符被注册到了主线程中epoll对象中。

4.5.4    Looper.cpp的addFd函数

int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
            events, callback.get(), data);
#endif
 
    if (!callback.get()) {
        if (! mAllowNonCallbacks) {
            ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
            return -1;
        }
 
        if (ident < 0) {
            ALOGE("Invalid attempt to set NULL callback with ident < 0.");
            return -1;
        }
    } else {
        ident = POLL_CALLBACK;
    }
 
    int epollEvents = 0;
    if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        Requestrequest;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;
 
        struct epoll_eventeventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;
 
        ssize_trequestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;

addFd中第2个参数为0,第3个参数是ALOOPER_EVENT_INPUT,第4个参数this,是NativeInputEventReceiver对象,也是LooperCallback对象类型,第5个参数是NULL

第一个if语句不成立,把ident设置成POLL_CALLBACK,表示调用者有回调方法会被执行。

int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;

设置监控事件为EPOLLIN可读事件

Requestrequest;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data; (base\services\core\jni) 58730 2015/6/6

用传递过来的参数初始化Request结构体变量request

struct epoll_eventeventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;

声明epoll机制事件结构体变量eventItem并初始化,设events为可读事件,注册的文件描述符为socket服务端fd

ssize_trequestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
 int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
 if (epollResult < 0) {
 ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
 return -1;
 }
 mRequests.add(fd, request);
} else {
 int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
 if (epollResult < 0) {
 ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
 return -1;
 }
 mRequests.replaceValueAt(requestIndex, request);
}

判断容器mRequests中是否有socket文件描述符fd,如果没有,就把fd注册到epoll对象mEpollFd中,否则,修改现有的epoll对象的兴趣列表中的fd,更新了eventItem。同时把调用者相关信息对象Request与fd作为一对映射关系保存到mRequests容器中,待执行epoll_wait时使用。

小结:

应用程序端管道inputChannels[1]注册在InputEventReceiver中,其socket对象注册到主线程epoll对象兴趣列表中,当socket对端即服务端inputChannels[0]上有数据写入后,应用程序便可从服务端socket对象上读取数据。

你可能感兴趣的:(Android,Framework)