【转载两篇关于android按键事件传递的文章,流程非常完整】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<Class<?>, Object>中

1.1.2    nativeInit方法实现

static jlongnativeInit(JNIEnv* env, jclassclazz,
        jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) {
    sp<MessageQueue> 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<jlong>(im);
}

android_os_MessageQueue_getMessageQueue方法:

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

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

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

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

NativeInputManager的构造函数:

NativeInputManager::NativeInputManager(jobjectcontextObj,
        jobjectserviceObj, const sp<Looper>& 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> 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<EventHubInterface>& eventHub,
    const sp<InputReaderPolicyInterface>& readerPolicy,
    const sp<InputDispatcherPolicyInterface>& 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<InputDispatcherPolicyInterface>& 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<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& 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<InputReaderInterface>& reader) :
    Thread(/*canCallJava*/ true), mReader(reader) {
}

把InputReader对象赋给mReader变量

InputDispatcherThread的构造函数:

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& 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<NativeInputManager*>(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<InputDeviceInfo> 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<KeyEntry*>(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<KeyEntry*>(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<InputTarget> 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>& 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>& 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<KeyEntry*>(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<KeyEntry*>(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<InputChannel> serverChannel;
    sp<InputChannel> 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<InputChannel>& outServerChannel, sp<InputChannel>& 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<NativeInputManager*>(ptr);
 
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }
 
    sp<InputWindowHandle> 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>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}

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

status_tInputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& 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> 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> 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<NativeInputChannel*>(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<InputQueue>(this), Looper.myQueue());

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

4.5.1    android_view_InputQueue.cpp的nativeInit

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

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

InputQueue* InputQueue::createQueue(jobjectinputQueueObj, const sp<Looper>& 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>& 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<InputEventReceiver>(this),
 inputChannel, mMessageQueue);
 
 mCloseGuard.open("dispose");
}

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

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

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

static jlongnativeInit(JNIEnv* env, jclassclazz, jobjectreceiverWeak,
        jobjectinputChannelObj, jobjectmessageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }
 
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    sp<NativeInputEventReceiver> 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<jlong>(receiver.get());
}
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
        jobjectreceiverWeak, const sp<InputChannel>& inputChannel,
        const sp<MessageQueue>& 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<LooperCallback>& 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对象上读取数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Google Android SDK开发范例大全(完整版)共4个分卷 目录 第1章 了解.深入.动手做. 1.1 红透半边天的Android 1.2 本书目的及涵盖范例范围 1.3 如何阅读本书 1.4 使用本书范例 1.5 参考网站 第2章 Android初体验 2.1 安装AndroidSDK与ADTplug-in 2.2 建立第一个Android项目(HelloAndroid!) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色常数的方法 3.3 更改TextView文字颜色——引用Drawable颜色常数及背景色 3.4 置换TextView文字——CharSequence数据类型与ResourceID应用 3.5 取得手机屏幕大小——DisplayMetrics取得画面宽高的方法 3.6 样式化的定型对象——Style样式的定义 3.7 简易的按钮事件——Button事件处理 3.8 手机页面的转换——setContentView的应用 3.9 调用另一个Activity——Intent对象的使用 3.10 不同Activity之间的数据传递——Bundle对象的实现 3.11 返回数据到前一个Activity——startActivityForResult方法 3.12 具有交互功能的对话框——AlertDialog窗口 3.13 置换文字颜色的机关——Button与TextView的交互 3.14 控制不同的文字字体——Typeface对象使用 3.15 如iPhone拖动相片特效——Gallery画廊 3.16 自制计算器——多按钮的整合应用 3.17 关于(About)程序信息——Menu功能菜单程序设计 3.18 程序加载中,请稍后——ProgressDialog与线程整合应用 3.19 全屏幕以按钮覆盖——动态产生按钮并最大化 3.20 今晚到哪儿打牙祭?——具选择功能的对话框 3.21 Android变脸——主题(Theme)实现 第4章 史上超豪华的手机控件 4.1 EditText与TextView共舞——setOnKeyListener事件 4.2 设计具有背景图的按钮——ImageButton的焦点及事件处理 4.3 给耶诞老人的信息——Toast对象的使用 4.4 我同意条款——CheckBox的isChecked属性 4.5 消费券采购列表——多选项CheckBox的应用 4.6 向左或向右——RadioGroup组与onCheckedChanged事件 4.7 专业相框设计——ImageView的堆栈应用 4.8 自定义下拉菜单模式——Spinner与setDropDownViewResource 4.9 动态添加/删除的Spinner菜单——ArrayList与Widget的依赖性 4.10 心爱小宝贝相片集——Gallery与衍生BaseAdapter容器 4.11 快速的搜索手机文件引擎——JavaI/O的应用 4.12 按钮也能随点击变换——ImageButton选择特效 4.13 具自动提示功能的菜单——AutoCompleteTextView与数组 4.14 数字及模拟小时钟设计——AnalogClock与DigitalClock的原理 4.15 动态输入日期与时间——DatePicker与TimePicker应用 4.16 猜猜红心A在那儿——ImageView点击事件与透明度处理 4.17 后台程序运行进度提示——ProgressBar与Handler的整合应用 4.18 动态文字排版——GridView与ArrayAdapter设计 4.19 在Activity里显示列表列表——ListView的布局 4.20 以动态列表配置选项——ListActivity与Menu整合技巧 4.21 查找程序根目录下所有文件——JavaI/O与ListActivity的结合.. 4.22 加载手机磁盘里的图文件——使用decodeFile方法 4.23 动态放大缩小ImageView里的图片——运用Matrix对象来缩放图文件 4.24 动态旋转图片——Bitmap与Matrix旋转ImageView 4.25 猜猜我在想什么——RadioButtonID 4.26 离开与关闭程序的弹出窗口——对话窗口上的ICON图标 第5章 交互式通信服务与手机控制 5.1 具有正则表达式的TextView——Linkify规则 5.2 ACTION!CALL!拨打电话——Intent.ACTION.CALL的使用 5.3 自制发送短信程序——SmsManager与PendingIntent对象 5.4 自制发送Email程序——Intent在Email上的使用 5.5 自制日历手机数据库——实现SQLiteOpenHelper 5.6 手机震动的节奏——Vibrator对象及周期运用 5.7 图文可视化提醒——Toast与LinearLayoutView 5.8 状态栏的图标与文字提醒——NotificationManager与Notification对象的应用 5.9 搜索手机通讯录自动完成——使用ContentResolver 5.10 取得联系人资料——Provider.Contact的使用 5.11 制作有图标的文件资源管理器——自定义Adapter对象 5.12 还原手机默认桌面——重写clearWallpaper方法 5.13 置换手机背景图——Gallery与setWallpaper整合实现 5.14 撷取手机现存桌面——getWallpaper与setImageDrawable 5.15 文件资源管理器再进化——JavaI/O修改文件名及删除 5.16 取得目前File与Cache的路径——getCacheDir与getFilesDir 5.17 打开/关闭WiFi服务——WifiManager状态判断 5.18 取得SIM卡内的信息——TelephonyManager的应用 5.19 调用拨号按钮——打电话CALL_BUTTON 5.20 DPAD按键处理——onKeyDown事件与Layout坐标交互 5.21 任务管理器正在运行的程序——RunningTaskInfo 5.22 动态更改屏幕方向——LANDSCAPE与PORTRAIT 5.23 系统设置更改事件——onConfigurationChanged信息处理 5.24 取得电信网络与手机相关信息——TelephonyManagerandroid.provider.Settings.System的应用 第6章 手机自动服务纪实 6.1 您有一条短信popup提醒——常驻BroadcastReceiver的应用 6.2 手机电池计量还剩多少——使用BroadcastReceiver捕捉Intent.ACTION_BATTERY_CHANGED 6.3 群发拜年短信给联系人——ACTION_PICK与Uri对象 6.4 开始与停止系统服务——Service与Runnable整合并用 6.5 通过短信发送email通知——BroadcastReceiver与Intent整合 6.6 手机拨接状态——PhoneStateListener之onCallStateChanged 6.7 有来电,发送邮件通知——PhoneStateListener与ACTION_SEND 6.8 存储卡剩余多少容量——Environment加StatFs 6.9 访问本机内存与存储卡——File的创建与删除 6.10 实现可定时响起的闹钟——PendingIntent与AlarmManager的运用 6.11 黑名单来电自动静音——PhoneStateListener与AudioManager 6.12 手机翻背面即静音震动——SensorListener及AudioManager整合应用 6.13 指定时间置换桌面背景——多AlarmManager事件处理 6.14 判断发送短信后的状态——BroadcastReceiver聆听PendingIntent 6.15 后台服务送出广播信息——sendBroadcast与BroadcastReceiver 6.16 开机程序设计——receiver与intent-filter协同作业 6.17 双向短信常驻服务——Service与receiver实例 第7章 娱乐多媒体 7.1 访问Drawable资源的宽高——ContextMenu与Bitmap的应用 7.2 绘制几何图形——使用android.graphics类 7.3 手机屏幕保护程序——FadeIn/FadeOut特效与运行线程 7.4 用手指移动画面里的照片——onTouchEvent事件判断 7.5 加载存储卡的Gallery相簿——FileArrayList 7.6 取得手机内置媒体里的图文件——ACTION_GET_CONTENT取回InputStream 7.7 相片导航向导与设置背景桌面——ImageSwitcher与Gallery 7.8 调整音量大小声——AudioManager控制音量 7.9 播放mp3资源文件——raw文件夹与MediaPlayer的使用 7.10 播放存储卡里的mp3音乐——MediaPlayer.setDataSource 7.11 自制录音/播放录音程序——MediaRecorder与AudioEncoder 7.12 通过收到短信开始秘密录音——MediaRecorder与BroadcastReceiver实例 7.13 内置影片播放器载入3gp电影——VideoViewWidget 7.14 自制3gp影片播放器——MediaPlayer与实现SurfaceView 7.15 相机预览及拍照临时文件——Camera及PictureCallback事件 第8章 当Android与Internet接轨 8.1 HTTPGET/POST传递参数——HTTP连接示范 8.2 在程序里浏览网页——WebView.loadUrl 8.3 嵌入HTML标记的程序——WebView.loadData 8.4 设计前往打开网页功能——Intent与Uri.parse 8.5 将网络图像网址放入Gallery中显示——URL.URLConnection.BaseAdapter 8.6 即时访问网络图文件展示——HttpURLConnection 8.7 手机气象局,实时卫星云图——HttpURLConnection与URLConnection和运行线程 8.8 通过网络播放MP3——Runnable存储FileOutputStream技巧 8.9 设置远程下载音乐为手机铃声——RingtoneManager与铃声存放路径 8.10 远程下载桌面背景图案——URLConnection与setWallpaper()搭配 8.11 将手机文件上传至网站服务器——模拟HTTPFORM的POSTACTION 8.12 移动博客发布器——以XML-RPC达成远程过程调用 8.13 移动RSS阅读器——利用SAXParser解析XML 8.14 远程下载安装Android程序——APKInstaller的应用 8.15 手机下载看3gp影片——Runnable混搭SurfaceView 8.16 访问网站LoginAPI——远程服务器验证程序运行权限 8.17 地震速报!——HttpURLConnection与Service侦测服务 第9章 Google服务与Android混搭 9.1 Google帐号验证Token——AuthSub 9.2 Google搜索——AutoCompleteTextView与GoogleSearchAPI 9.3 前端产生QRCode二维条形码——GoogleChartAPI 9.4 以经纬度查找目的地位置——GeoPoint与MapView的搭配运用 9.5 GPSGoogle地图——LocationListener与MapView实时更新 9.6 移动版GoogleMap——Geocoder反查Address对象 9.7 规划导航路径——DirectionsRoute 9.8 移动设备上的Picasa相册——GooglePicasaAPI 9.9 随身翻译机——GoogleTranslateAPI 第10章 创意Android程序设计 10.1 手机手电筒——PowerManager控制WakeLock并改变手机亮度 10.2 GPS轨迹记录器——利用LocationListener在地图上画图并换算距离 10.3 女性贴身看护——AlarmManager.DatePicker.TimePicker 10.4 手机QRCode二维条形码生成器——Canvas与SurfaceHolder绘图 10.5 AndroidQRCode二维条形码扫描仪——BitmapFactory.decodeByteArray 10.6 上班族今天中午要吃什么——热量骰子地图 10.7 掷杯筊——把手机放在空中甩事件处理...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值