做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. 总结
要分析输入系统,最好先从输入事件系统服务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
1.1.2 nativeInit方法实现
static jlongnativeInit(JNIEnv* env, jclassclazz,
jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) {
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast(im);
}
android_os_MessageQueue_getMessageQueue方法:
sp android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast(ptr);
}
获得了C++层NativeMessageQueue对象,NativeMessageQueue对象是在创建Looper对象时创建的,在本博客Handler机制中已经分析过
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast(im);
创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;getLooper()获得c++层的Looper对象,在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中
NativeInputManager的构造函数:
NativeInputManager::NativeInputManager(jobjectcontextObj,
jobjectserviceObj, const sp& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mContextObj = env->NewGlobalRef(contextObj);
mServiceObj = env->NewGlobalRef(serviceObj);
{
AutoMutex_l(mLock);
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
}
sp eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法;创建c++层中全局变量mContextObj, mServiceObj;创建EventHub对象并作为参数创建InputManager对象。
1.1.3 EventHub.cpp的构造函数
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
// acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
int major, minor;
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP was introduced in kernel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
初始化必要的变量,采用epoll机制系统调用接口epoll_create创建epoll对象,返回文件描述符mEpollFd,代表epoll对象;采用inotify机制监听文件或目录的移动、读取、写入或删除等事件,inotify机制主要包含2个系统调用接口:inotify_init, inotify_add_watch
inotify_init:创建一个inotify对象,如果成功,返回一个文件描述符,作为该对象;返回-1表示出错
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)
inotify_add_watch:把监控项添加到mINotifyFd对象的监控列表中,第二个参数DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;第三个参数是一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。IN_DELETE:当被监控目录内删除文件或目录时触发该事件;IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。
如果inotify_add_watch执行成功,返回一个非负监控描述符(假如为wd),代表被监控的项,如果将来不需要再监听了,可以使用inotify_rm_watch(fd, wd)删除所添加的监控项wd。
struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
把inotify对象mINotifyFd添加到epoll对象的兴趣列表中,此处采用inotify与epoll机制结合起来检查文件
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
创建了管道对象读取端文件描述符mWakeReadPipeFd并添加到epoll对象的兴趣列表中
读到此处,提出2个问题:
Q1 epoll机制一般有3个系统调用接口,而截止到此处,只提到epoll_create, epoll_ctl,还缺epoll_wait,猜测在代码某处肯定有epoll_wait,否则epoll机制无法工作
Q2 如果找到了epoll_wait,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用
记住这两个问题,有助于在分析多进程控制时的代码走向
1.1.4 InputManager.cpp的构造器
mInputManager = new InputManager(eventHub, this, this);
InputManager::InputManager(
const sp& eventHub,
const sp& readerPolicy,
const sp& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
第一个参数是刚创建的EventHub对象,第二、三个参数都是this,当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager
用这两个传递过去的对象类型作为参数分别创建了InputDispatcher和InputReader对象,再调用initialize方法分别创建了与InputDispatcher和InputReader对应的线程InputDispatcherThread和InputReaderThread对象
1.1.5 InputDispatcher的构造函数
InputDispatcher::InputDispatcher(const sp& policy) :
mPolicy(policy),
mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(NULL),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
mKeyRepeatState.lastKeyEntry = NULL;
policy->getDispatcherConfiguration(&mConfig);
}
把传递过来的NativeInputManager对象赋给mPolicy,该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型,然后又初始化了很多变量,其中包括:
mLooper = new Looper(false);
创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。
注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。
Q3 既然有了epoll_ctl,那肯定某处会调用epoll_wait
1.1.6 InputReader的构造函数
InputReader::InputReader(const sp& eventHub,
const sp& policy,
const sp& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
AutoMutex_l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
第一个参数是EventHub对象,第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型
mQueuedListener = new QueuedInputListener(listener);
把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量
1.1.7 InputManager.cpp的initialize函数
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
用InputReader对象作为参数创建InputReaderThread线程对象,用InputDispatcher作为参数创建InputDispatcherThread线程对象,InputReaderThread的构造函数:
InputReaderThread::InputReaderThread(const sp& reader) :
Thread(/*canCallJava*/ true), mReader(reader) {
}
把InputReader对象赋给mReader变量
InputDispatcherThread的构造函数:
InputDispatcherThread::InputDispatcherThread(const sp& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}
把InputDispatcher对象赋给mDispatcher变量
进程执行到此处,InputManagerService的初始化就完成,整个过程都是在创建、初始化各种对象。
Q4 既然创建了线程对象,那么就应该在某处启动线程,否则线程无法运行,在哪里启动线程
1.2 WindowManagerService的main方法
wm = WindowManagerService.main(context, inputManager, mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore);
public static WindowManagerServicemain(final Contextcontext,
final InputManagerServiceim,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = new WindowManagerService(context, im,
haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
mInputManager = inputManager;
main方法创建了WindowManagerService对象,第二个参数是InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager
1.3 ServiceManager的addService方法
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
把InputManagerService服务注册到ServiceManager中
1.4 InputManagerService的setWindowManagerCallbacks方法
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
把InputMonitor对象传递到InputManagerService中去,方便回调,比如7.2节通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法
1.5 InputManagerService的start方法
start方法中有两句:
nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);
nativeStart的本地实现:
static void nativeStart(JNIEnv* env, jclassclazz, jlongptr) {
NativeInputManager* im = reinterpret_cast(ptr);
status_tresult = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
ptr在1.1.1节已经提到过,是NativeInputManager对象地址,只不过是长整形,此处再转化成NativeInputManager型指针
Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调
Q5 创建了看门狗,有什么用?
nativeStart的传递过程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart —-> InputManager.cpp的start()
1.5.1 InputManager.cpp的start
status_tInputManager::start() {
status_tresult = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
ALOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
首先启动分发器线程InputDispatcherThread,调用run方法后,开始执行threadLoop方法,如果threadLoop返回true,就再次执行threadLoop方法直到requestExit方法停止线程;再调用InputReaderThread的run方法启动接收器线程。这回答了1.1.7节Q4问题,只要创建了线程,那就应该有启动线程的地方。
threadLoop中调用了mDispatcher->dispatchOnce()
1.5.2 InputDispatcher.cpp的dispatchOnce
void InputDispatcher::dispatchOnce() {
nsecs_tnextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex_l(mLock);
mDispatcherIsAliveCondition.broadcast();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_tcurrentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
一开始,CommandQueue队列中没有任何命令,haveCommandsLocked为false,if语句成立,执行dispatchOnceInnerLocked
1.5.2.1 InputDispatcher.cpp的dispatchOnceInnerLocked
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
// Nothing to do if there is no pending event.
if (!mPendingEvent) {
return;
}
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.dequeueAtHead();
traceInboundQueueLengthLocked();
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}
// Get ready to dispatch the event.
resetANRTimeoutsLocked();
}
mPendingEvent初始化为空,mInboundQueue队列也为空,下面语句:
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
成立,进入到if语句中
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false
mKeyRepeatState.lastKeyEntry初始化为false,跳过:
if (mKeyRepeatState.lastKeyEntry) {
这条语句,继续:
if (!mPendingEvent) {
return;
}
mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回到dispatchOnce方法中继续后面语句的执行:
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
一开始,没有命令待处理,runCommandsLockedInterruptible返回false,跳过语句;如果有命令的话,就取出来处理
1.5.2.2 Looper的pollOnce
mLooper->pollOnce(timeoutMillis);
看到熟悉的语句了,pollOnce一直执行到epoll_wait监控管道读取端,如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;如果还没有事件,进程InputDispatcherThread在此等待,这就回答了Q3
Q6 既然有Looper的pollOnce方法,就有对应的唤醒方法,在哪里?
1.5.3 InputReader.cpp的loopOnce
void InputReader::loopOnce() {
int32_toldGeneration;
int32_ttimeoutMillis;
bool inputDevicesChanged = false;
Vector inputDevices;
{ // acquire lock
AutoMutex_l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_tchanges = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex_l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
if (mNextTimeout != LLONG_MAX) {
nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
InputDeviceInfo:描述输入设备特征和特性的类
getEvents用来获取输入设备信息、输入事件;processEventsLocked用来处理获得的事件的;按照顺序,首先进入到getEvents
1.5.3.1-1 EventHub.cpp的getEvents函数
size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_tbufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex_l(mLock);
struct input_eventreadBuffer[bufferSize];
RawEvent* event = buffer;
size_tcapacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_tnow = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
// Report any devices that had last been added/removed.
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
deletedevice;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
ALOGV("Reporting device opened: id=%d, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
event->when = now;
event->type = FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
ALOGV("awoken after wake()");
awoken = true;
char buffer[16];
ssize_tnRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
ssize_tdeviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0) {
ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
eventItem.events, eventItem.data.u32);
continue;
}
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
int32_treadSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %" PRId32
" bufferSize: %zu capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
ALOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size: %d)", readSize);
} else {
int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_tcount = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
// Some input devices may have a better concept of the time
// when an input event was actually generated than the kernel
// which simply timestamps all events on entry to evdev.
// This is a custom Android extension of the input protocol
// mainly intended for use with uinput based device drivers.
if (iev.type == EV_MSC) {
if (iev.code == MSC_ANDROID_TIME_SEC) {
device->timestampOverrideSec = iev.value;
continue;
} else if (iev.code == MSC_ANDROID_TIME_USEC) {
device->timestampOverrideUsec = iev.value;
continue;
}
}
if (device->timestampOverrideSec || device->timestampOverrideUsec) {
iev.time.tv_sec = device->timestampOverrideSec;
iev.time.tv_usec = device->timestampOverrideUsec;
if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
device->timestampOverrideSec = 0;
device->timestampOverrideUsec = 0;
}
ALOGV("applied override time %d.%06d",
int(iev.time.tv_sec), int(iev.time.tv_usec));
}
#ifdef HAVE_POSIX_CLOCKS
// Use the time specified in the event instead of the current time
// so that downstream code can get more accurate estimates of
// event dispatch latency from the time the event is enqueued onto
// the evdev client buffer.
//
// The event's timestamp fortuitously uses the same monotonic clock
// time base as the rest of Android. The kernel event device driver
// (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().
// The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere
// calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a
// system call that also queries ktime_get_ts().
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+ nsecs_t(iev.time.tv_usec) * 1000LL;
ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);
// Bug 7291243: Add a guard in case the kernel generates timestamps
// that appear to be far into the future because they were generated
// using the wrong clock source.
//
// This can happen because when the input device is initially opened
// it has a default clock source of CLOCK_REALTIME. Any input events
// enqueued right after the device is opened will have timestamps
// generated using CLOCK_REALTIME. We later set the clock source
// to CLOCK_MONOTONIC but it is already too late.
//
// Invalid input event timestamps can result in ANRs, crashes and
// and other issues that are hard to track down. We must not let them
// propagate through the system.
//
// Log a warning so that we notice the problem and recover gracefully.
if (event->when >= now + 10 * 1000000000LL) {
// Double-check. Time may have moved on.
nsecs_ttime = systemTime(SYSTEM_TIME_MONOTONIC);
if (event->when > time) {
ALOGW("An input event from %s has a timestamp that appears to "
"have been generated using the wrong clock source "
"(expected CLOCK_MONOTONIC): "
"event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ". "
"Using current time instead.",
device->path.string(), event->when, time, now);
event->when = time;
} else {
ALOGV("Event time is ok but failed the fast path and required "
"an extra call to systemTime: "
"event time %" PRId64 ", current time %" PRId64
", call time %" PRId64 ".",
event->when, time, now);
}
}
#else
event->when = now;
#endif
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
mPendingEventIndex -= 1;
break;
}
}
} else if (eventItem.events & EPOLLHUP) {
ALOGI("Removing device %s due to epoll hang-up event.",
device->identifier.name.string());
deviceChanged = true;
closeDeviceLocked(device);
} else {
ALOGW("Received unexpected epoll event 0x%08x for device %s.",
eventItem.events, device->identifier.name.string());
}
}
// readNotify() will modify the list of devices so this must be done after
// processing all other events to ensure that we read all remaining events
// before closing the devices.
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) {
continue;
}
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
// Poll for events. Mind the wake lock dance!
// We hold a wake lock at all times except during epoll_wait(). This works due to some
// subtle choreography. When a device driver has pending (unread) events, it acquires
// a kernel wake lock. However, once the last pending event has been read, the device
// driver will release the kernel wake lock. To prevent the system from going to sleep
// when this happens, the EventHub holds onto its own user wake lock while the client
// is processing events. Thus the system can only sleep if there are no events
// pending or currently being processed.
//
// The timeout is advisory only. If the device is asleep, it will not wake just to
// service the timeout.
mPendingEventIndex = 0;
mLock.unlock(); // release lock before poll, must be before release_wake_lock
release_wake_lock(WAKE_LOCK_ID);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
// An error occurred.
mPendingEventCount = 0;
// Sleep after errors to avoid locking up the system.
// Hopefully the error is transient.
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
}
}
// All done, return the number of events we read.
return event - buffer;
}
第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件
第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:
struct input_event {
struct timevaltime;
__u16type;
__u16code;
__s32value;
};
time:事件的执行时间
type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等
code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)
value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值
mNeedToReopenDevices,mClosingDevices一开始初始化为false,跳过:
if (mNeedToReopenDevices) {
与
while (mClosingDevices) {
mNeedToScanDevices初始化为true,执行到:
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
mNeedToScanDevices初始化为true,scanDevicesLocked的调用过程:scanDevicesLocked() —-> scanDirLocked(DEVICE_PATH) —-> openDeviceLocked(devname)
DEVICE_PATH被赋值为/dev/input,这是输入设备节点目录,该目录下包含所有输入设备节点,在命令行下进入该目录看看,一般包含:
crw-rw---- root input 13, 64 1970-01-01 08:00 event0
crw-rw---- root input 13, 65 1970-01-01 08:00 event1
crw-rw---- root input 13, 66 1970-01-01 08:00 event2
crw-rw---- root input 13, 67 1970-01-01 08:00 event3
crw-rw---- root input 13, 68 1970-01-01 08:00 event4
crw-rw---- root input 13, 69 1970-01-01 08:00 event5
crw-rw---- root input 13, 70 1970-01-01 08:00 event6
crw-rw---- root input 13, 71 1970-01-01 08:00 event7
event0一般代表按键或键盘,可用命令cat /proc/bus/input/device查看所有输入设备详细信息
1.5.3.2 EventHub.cpp的openDeviceLocked函数
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0
open以读写方式打开该设备文件,返回设备文件描述符fd
然后做了一系列动作:获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,表示当前发生输入事件的设备是按键或键盘;
在这段代码之后,系统做了很多事情,比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;如果是键盘、操纵杆等,就配置其布局。这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。
// Register with epoll.
struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = mUsingEpollWakeup ? EPOLLIN : EPOLLIN | EPOLLWAKEUP;
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
deletedevice;
return -1;
}
把设备文件描述符注册到epoll对象中,如果linux内核版本号大于或等于3.5,就设置监控事件为EPOLLIN,表示可读事件,并把设备id保存到data结构体中。
addDeviceLocked(device);
void EventHub::addDeviceLocked(Device* device) {
mDevices.add(device->id, device);
device->next = mOpeningDevices;
mOpeningDevices = device;
}
最后调用addDeviceLocked(device)把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices,mOpeningDevices初始化为0,表示下一个设备还没有,同时设mOpeningDevices为当前设备对象。
openDeviceLocked函数执行完毕返回到getEvents的scanDevicesLocked()函数后继续执行
1.5.3.1-2 EventHub.cpp的getEvents函数
mOpeningDevices不为空,进入到while循环:
while (mOpeningDevices != NULL) {
该while循环把所有设备Device的内容保存到RawEvent结构体中,把event->type设为DEVICE_ADDED,意味着事件被成功添加到RawEvent,再把mNeedToSendFinishedDeviceScan设为true,意味着结束设备扫描。继续执行到:
while (mPendingEventIndex < mPendingEventCount) {
mPendingEventCount和mPendingEventIndex都初始化为0,while循环不成立,跳过该while循环
mPendingINotify初始化为false,跳过:
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
deviceChanged也为false,跳过:
if (deviceChanged) {
执行到:
if (event != buffer || awoken) {
break;
}
不相等,break跳出for循环执行到最后event-buffer得到输入设备的个数,返回到loopOnce中执行processEventsLocked —> addDeviceLocked把设备添加到KeyedVector中,同时设置一些变量,比如把keyboardSource为AINPUT_SOURCE_KEYBOARD,表示按键
因为InputReaderThread是循环执行的,进程又一次进入到getEvents函数中,开始执行for循环,这时候设备都已经取出来了就不再执行scanDevicesLocked()扫描获取设备
mPendingEventCount还是为0,所以:
while (mPendingEventIndex < mPendingEventCount) {
仍然不成立,进程执行到:
mPendingEventIndex = 0;
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
一开始,队列等待中的事件都为0,mPendingEventIndex设为0;调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,InputRead线程处于等待状态,等待被唤醒,什么时候被唤醒
在1.1.3节,提到Q1和Q2两个问题,Q1在此处可以解答。
要想回答Q2,就必须要清楚epoll兴趣列表中注册了哪些文件描述符,在1.1.3节可知,注册了mINotifyFd, mWakeReadPipeFd和mWakeWritePipeFd;由1.5.3.2可知,注册了设备文件描述符fd。要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。搜索一下代码,发现EventHub.cpp的wake()方法中有管道写入端描述符mWakeWritePipeFd,这是一个线索:
1.5.3.3 EventHub.cpp的wake()
void EventHub::wake() {
ALOGV("wake() called");
ssize_tnWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1 && errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
在wake方法中,管道写入端mWakeWritePipeFd写入了一个字符w,管道中有数据了,epoll机制监控到管道有I/O事件发生,于是epoll_wait执行,返回发生I/O事件的个数,并且把发生的事件放在epoll_event结构数组mPendingEventItems中,这时调用者线程InputReaderThread被唤醒,这就回答了1.1.3节Q2问题,但是新的问题又来了
Q7:wake方法是什么被调用的?被谁调用的?
先继续epoll_wait方法后面的代码分析
1.5.3.1-3 EventHub.cpp的getEvents函数
epoll_wait返回发生事件的个数赋给pollResult,pollResult肯定大于0,执行:
mPendingEventCount = size_t(pollResult);
pollResult赋给了mPendingEventCount,表明有事件待处理
再次执行getEvents中的for循环
mOpeningDevices在1.5.3.1-1节中已经被赋为device->next即0,所以while循环:
while (mClosingDevices) {
不成立,跳过。
mPendingEventCount大于0了,mPendingEventIndex < mPendingEventCount成立,进入到下面while循环:
while (mPendingEventIndex < mPendingEventCount) {
根据发生事件的类型,判断:
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
成立,进程执行到:
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
从管道中读取数据,该数据就是一个字符w,执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;然后1.5.3.3节的wake()函数又调用write写入字符w触发了epoll_wait,唤醒了InputReader线程,然后又开始执行for循环……
Q8 这样循环下去的意义何在?
循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句:
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
成立,跳出for循环返回了,如果此时有事件发生的话,因为epoll_wait还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。
1.5.3.1-4 EventHub.cpp的getEvents函数
在1.5.3.2节openDeviceLocked函数中,把输入设备文件描述符注册到mEpollFd中,因此,当有输入事件发生时,epoll_wait方法开始执行返回发生的个数赋给pollResult变量,此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环,进入到:
while (mPendingEventIndex < mPendingEventCount) {
此时eventItem.data.u32的值是设备id,if语句:
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
与
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
都不成立,进程执行到:
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
int32_treadSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中,如果没出错的话,执行:
int32_tdeviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_tcount = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
struct input_event& iev = readBuffer[i];
计算出有多少个事件并取出来保存到input_event结构体变量iev中,然后计算相应的事件执行时间,再把input_event结构体变量中的内容保存到原始事件RawEvent中,然后循环直到所有事件都被获取到后,进程执行:
// Return now if we have collected any events or if we were explicitly awoken.
if (event != buffer || awoken) {
break;
}
event指向的内存现在已经保存了事件,而buffer还是指向首地址,不相等,if成立,执行break跳出for(::)无限循环,执行到getEvents最后一句:
return event - buffer;
返回读取事件的个数,并返回到InputReader的loopOnce中这一句:
size_tcount = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
count大于0,执行:
if (count) {
processEventsLocked(mEventBuffer, count);
}
调用processEventsLocked对事件进行处理,此处属于事件处理阶段,在第2节分析。
第1节做了3件事:a. 创建InputDispatchThread, InputReaderThread,InputManager等主要输入系统核心组件
b. 扫描所有输入设备,获取输入设备各种信息
c. 获取输入设备事件
第1节Framework层如何获取事件的过程分析完毕,还留下Q6问题
通过getEvents获得事件后,执行过程:processEventsLocked —-> processEventsForDeviceLocked
在processEventsLocked中,rawEvent->type表示事件的类型,有如下几种:
EV_KEY,EV_ABS,EV_REL,EV_SW,EV_LED,EV_FF,...
本文针对按键事件,因此type是EV_KEY,小于FIRST_SYNTHETIC_EVENT,
2.1 InputReader.cpp的processEventsForDeviceLocked函数
void InputReader::processEventsForDeviceLocked(int32_tdeviceId,
const RawEvent* rawEvents, size_tcount) {
ssize_tdeviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
}
从KeyedVector容器中根据设备id号取出设备对象赋给device,参数count为多少?正常情况下按键按下一次,产生一个按下事件,类型为EV_KEY,还有一个同步事件EV_SYNC,因此count为2
然后调用过程为:
device->process(rawEvents, count) —-> mapper->process(rawEvent) —-> KeyboardInputMapper::processKey
不同的输入时间对应不同的InputMapper对象,按键事件对应的是KeyboardInputMapper对象,就执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。
再根据按键类型,执行到KeyboardInputMapper::processKey函数,在processKey中,主要代码在最后部分:
int32_toldMetaState = mMetaState;
int32_tnewMetaState = updateMetaState(keyCode, down, oldMetaState);
bool metaStateChanged = oldMetaState != newMetaState;
if (metaStateChanged) {
mMetaState = newMetaState;
updateLedState(false);
}
nsecs_tdownTime = mDownTime;
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards, the key layout file should specify the policy flags for
// each wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
if (down && getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE;
}
if (mParameters.handlesKeyRepeat) {
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
if (metaStateChanged) {
getContext()->updateGlobalMetaState();
}
if (down && !isMetaKey(keyCode)) {
getContext()->fadePointer();
}
NotifyKeyArgsargs(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
getListener()->notifyKey(&args);
NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:
nsecs_t eventTime:按键事件的发生时间
int32_t deviceId:按键的设备id
uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘
uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。
int32_t action:按键动作标志,是按下按键还是松开按键
int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM
int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置
int32_t scanCode:原始按键值,就是从底层获取到的按键值
int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0
nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量
getListener()->notifyKey(&args);
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
mQueuedListener是在1.1.6节创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数,如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。
2.2 InputDispatcher.cpp的notifyKey函数
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
"flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
args->eventTime, args->deviceId, args->source, args->policyFlags,
args->action, args->flags, args->keyCode, args->scanCode,
args->metaState, args->downTime);
#endif
if (!validateKeyEvent(args->action)) {
return;
}
uint32_tpolicyFlags = args->policyFlags;
int32_tflags = args->flags;
int32_tmetaState = args->metaState;
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
if (policyFlags & POLICY_FLAG_FUNCTION) {
metaState |= AMETA_FUNCTION_ON;
}
policyFlags |= POLICY_FLAG_TRUSTED;
int32_tkeyCode = args->keyCode;
if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
int32_tnewKeyCode = AKEYCODE_UNKNOWN;
if (keyCode == AKEYCODE_DEL) {
newKeyCode = AKEYCODE_BACK;
} else if (keyCode == AKEYCODE_ENTER) {
newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
AutoMutex_l(mLock);
struct KeyReplacementreplacement = {keyCode, args->deviceId};
mReplacedKeys.add(replacement, newKeyCode);
keyCode = newKeyCode;
metaState &= ~AMETA_META_ON;
}
} else if (args->action == AKEY_EVENT_ACTION_UP) {
// In order to maintain a consistent stream of up and down events, check to see if the key
// going up is one we've replaced in a down event and haven't yet replaced in an up event,
// even if the modifier was released between the down and the up events.
AutoMutex_l(mLock);
struct KeyReplacementreplacement = {keyCode, args->deviceId};
ssize_tindex = mReplacedKeys.indexOfKey(replacement);
if (index >= 0) {
keyCode = mReplacedKeys.valueAt(index);
mReplacedKeys.removeItemsAt(index);
metaState &= ~AMETA_META_ON;
}
}
KeyEventevent;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
bool needWake;
{ // acquire lock
mLock.lock();
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
mLock.lock();
}
int32_trepeatCount = 0;
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
该函数做了很重要的任务:前半部分对传递过来的按键事件进行检查、验证,之后处理特殊按键,再把按键放到InboundQueue队列中,最后调用Looper对象的wake像管道中写入字符唤醒InputDispatcherThread线程
if (!validateKeyEvent(args->action)) {
return;
}
检查按键是否按下、松开
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件,比如手机上除了屏幕显示外的MENU、HOME、BACK键
if (policyFlags & POLICY_FLAG_FUNCTION) {
metaState |= AMETA_FUNCTION_ON;
}
设置metaState为meta键标志
policyFlags |= POLICY_FLAG_TRUSTED;
设置该标志表示按键事件是可信任的,比如,一个直接与Android机器相连接的按键设备
KeyEventevent;
event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime);
把参数传递给KeyEvent对象event
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等,这一步将在第7节分析
needWake = enqueueInboundEventLocked(newEntry);
创建KeyEntry对象并把按键事件作为该对象的内容,enqueueInboundEventLocked函数根据输入事件的类型作进一步处理:
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
case EventEntry::TYPE_KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
mInboundQueue一开始没有数据为空,enqueueAtTail函数把按键事件对象KeyEntry插入到mInboundQueue队尾。
case语句用来切换app,如果是HOME、挂号按键,就把needWake设为true,表示可以唤醒InputDispatcherThread来分发按键事件,否则,不唤醒InputDispatcherThread,那么按键就不会处理
if (needWake) {
mLooper->wake();
}
wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程是谁?在1.5.2.2节,InputDispatcherThread线程执行到了Looper对象的pollOnce函数时处于阻塞状态,此时epoll机制检测到管道中有数据写入事件发生,由这篇文章:Handler机制可知,读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread,Q6也就有了答案。
小结:
把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。
现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。
由2.2节可知,InputDispatcherThread线程被唤醒后,又一次地执行dispatchOnce函数,在dispatchOnce中调用了dispatchOnceInnerLocked函数
3.1 InputDispatcher.cpp的dispatchOnceInnerLocked
mInboundQueue中已经有了按键事件,不为空,执行到这句:
else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.dequeueAtHead();
}
dequeueAtHead函数从队列mInboundQueue头部取出一个元素EventEntry对象交给mPendingEvent,EventEntry代表按键事件
在switch语句中,根据类型来选择执行语句,按键为TYPE_KEY
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast(mPendingEvent);
......
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
忽略不重要的代码,下一步调用dispatchKeyLocked函数:
if (! entry->dispatchInProgress) {
if (entry->repeatCount == 0
&& entry->action == AKEY_EVENT_ACTION_DOWN
&& (entry->policyFlags & POLICY_FLAG_TRUSTED)
&& (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
// driver is automatically generating key repeats itself. We take note of the
// repeat here, but we disable our own next key repeat timer since it is clear that
// we will not need to synthesize key repeats ourselves.
entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
} else {
// Not a repeat. Save key down state in case we do see a repeat later.
resetKeyRepeatLocked();
mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
}
mKeyRepeatState.lastKeyEntry = entry;
entry->refCount += 1;
} else if (! entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
if (entry->repeatCount == 1) {
entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
} else {
entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
}
entry->dispatchInProgress = true;
logOutboundKeyDetailsLocked("dispatchKey - ", entry);
}
先判断是否包含重复按键事件,如果是,暂时先清除掉,然后计算repeatCount值;如果没有重复按键事件,就保存按键状态、执行时间;如果计算后的repeatCount为1,设flags为AKEY_EVENT_FLAG_LONG_PRESS,表示长按事件。
entry->dispatchInProgress = true;
把dispatchInProgress设为true,意味着即将进行分发事件了
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindowHandle != NULL) {
commandEntry->inputWindowHandle = mFocusedWindowHandle;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
}
entry->interceptKeyResult一开始默认为INTERCEPT_KEY_RESULT_UNKNOWN,if语句成立
如果entry->policyFlags为POLICY_FLAG_PASS_TO_USER,表示按键需要传递给应用程序;doInterceptKeyBeforeDispatchingLockedInterruptible函数也是拦截按键处理的方法,在7.2节详解
// Identify targets. VectorinputTargets; int32_tinjectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); setInjectionResultLocked(entry, injectionResult);
InputTarget结构体:指定一个输入事件如何被分发到一个特定窗口。该结构体包含了很多特征变量:x,y坐标,输入事件通道InputChannel等;findFocusedWindowTargetsLocked找到获得焦点的窗口,如果成功,返回值设为INPUT_EVENT_INJECTION_SUCCEEDED,并把获得焦点的窗口的输入通道保存到InputTarget对象中,再把InputTarget对象放到inputTargets堆栈顶,setInjectionResultLocked函数把该获得焦点的标志INPUT_EVENT_INJECTION_SUCCEEDED保存到InjectionState结构体元素injectionResult中
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
dispatchEventLocked函数从inputTarget容器中取出获得焦点的输入通道管理对象Connection,Connection用来管理单个输入通道InputChannel的分发状态。dispatchEventLocked函数的调用过程:
dispatchEventLocked —-> prepareDispatchCycleLocked —-> enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_tcurrentTime,
const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
startDispatchCycleLocked(currentTime, connection);
}
}
outboundQueue一开始为空,当执行完enqueueDispatchEntryLocked后,outboundQueue就有了按键事件DispatchEntry,不为空,所以if语句:
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
成立,startDispatchCycleLocked开始发送事件,先看enqueueDispatchEntryLocked:
enqueueDispatchEntryLocked函数相关代码:
void InputDispatcher::enqueueDispatchEntryLocked(
const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_tdispatchMode) {
int32_tinputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return;
}
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast(eventEntry);
dispatchEntry->resolvedAction = keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
if (!connection->inputState.trackKey(keyEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
connection->getInputChannelName());
#endif
deletedispatchEntry;
return; // skip the inconsistent event
}
break;
}
......
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
incrementPendingForegroundDispatchesLocked(eventEntry);
}
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
最后一个参数dispatchMode针对触摸的,与触摸相关的代码暂时去掉
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
创建DispatchEntry按键事件对象,把该对象作为一个发送数据包加入到outboundQueue队列中
trackKey函数把按键分为按下和松开两类事件处理,如果是按下就就先保存到KeyMemento结构体中,如果是在松开状态下,该事件是回退事件,即不在应用程序中处理的事件,就从回退事件集合中删除该按键
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
再把新构建的按键事件数据包添加到outboundQueue队列尾部即栈顶。
startDispatchCycleLocked函数:
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
dispatchEntry->deliveryTime = currentTime;
// Publish the event.
status_tstatus;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast(eventEntry);
// Publish the key event.
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
}
当outboundQueue非空并且其他条件都具备时(比如,inputchannel已经注册,通信没有错误等),取出outboundQueue队列头元素,赋给dispatchEntry,再取出事件对象KeyEntry,根据事件类型确定case语句分支,如果是按键事件,就调用connection的InputPublisher的publishKeyEvent函数发送到inputchannel中,如果publishKeyEvent返回0,表示按键事件发送成功
3.2 InputTransport.cpp的publishKeyEvent
status_tInputPublisher::publishKeyEvent(
uint32_tseq,
int32_tdeviceId,
int32_tsource,
int32_taction,
int32_tflags,
int32_tkeyCode,
int32_tscanCode,
int32_tmetaState,
int32_trepeatCount,
nsecs_tdownTime,
nsecs_teventTime) {
#if DEBUG_TRANSPORT_ACTIONS
ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
"action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(), seq,
deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
return BAD_VALUE;
}
InputMessagemsg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
创建Message消息,把传递过来的事件信息保存到Message对象中,调用服务端管道InputChannel的sendMessage方法发送消息,sendMessage源码:
status_tInputChannel::sendMessage(const InputMessage* msg) {
size_tmsgLength = msg->size();
ssize_tnWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),
msg->header.type, error);
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
return DEAD_OBJECT;
}
return -error;
}
if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
mName.string(), msg->header.type);
#endif
return DEAD_OBJECT;
}
#if DEBUG_CHANNEL_MESSAGES
ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endif
return OK;
}
send系统调用相当于write调用,区别在于:send专用于socket文件描述符;send多了一个flags参数表示I/O事件的行为。send向服务端fd发送消息msg,如果成功,返回成功发送消息的大小;返回-1表示发送了错误
在3.4.1.1节通过socketpair创建一对socket对象,一个用作服务端,一个用作客户端,并且是已经连接好的插口,无需再通过bind、connect去连接,两端都可以发送、接收,全双工通信。在按键事件中,用socket[0]作为服务端,通过send向服务端发送数据,recv从客户端socket[1]接收数据。
在3.5.3.1节,socket客户端文件描述符被注册到了主线程epoll兴趣列表中,如果socket服务端没有往inputchannel中写入数据,客户端暂时处于阻塞状态,一旦服务端有了数据后,客户端也被唤醒了,客户端唤醒后的处理过程在第5节分析。
Q9 在publishKeyEvent中调用这句时:
return mChannel->sendMessage(&msg)
mChannel是服务端管道对象,该对象如何创建、注册的?socket文件描述符mFd什么时候创建;既然有服务端,肯定有客户端来接收数据,那么应用程序客户端socket如何创建?应用程序客户端InputChannel对象又是什么时候创建、注册的?
关于此问题将在第4节分析
涉及到应用程序相关的,就从ViewRootImpl来分析
4.1 ViewRootImpl.java的setView方法
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
当窗口没有按键事件传输通道时,创建一个InputChannel对象,相当于一个管道,其他进程通过该管道像窗口发送按键事件消息。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
mWindowSession在ViewRootImpl构造器中创建:
mWindowSession = WindowManagerGlobal.getWindowSession();
4.2 WindowManagerGlobal.java的getWindowSession方法
public static IWindowSessiongetWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManagerimm = InputMethodManager.getInstance();
IWindowManagerwindowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
getWindowManagerService方法源码:
public static IWindowManagergetWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
sWindowManagerService = getWindowManagerService();
ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e);
}
}
return sWindowManagerService;
}
}
getWindowSession方法做了很多工作,单例类InputMethodManager通过getInstance返回其唯一的对象,InputMethodManager表示输入法框架系统级API,用以输入法和应用程序之间的交互。
getService(“window”)获得一个WindowManagerService服务的Binder对象,实际是BinderProxy对象,asInterface接口根据BinderProxy对象生成Proxy客户端代理对象,返回到getWindowSession中赋给IWindowManager接口类型windowManager变量,然后利用该代理对象来访问WindowManagerService服务端中的方法openSession,其参数涵义:
第一个参数是一个IWindowSessionCallback.Stub对象
第二个参数imm.getClient(),返回IInputMethodClient.Stub对象
第三个参数imm.getInputContext(),返回ControlledInputConnectionWrapper对象
在openSession方法中,创建了Session对象,并把这三个参数及WindowManagerService对象传递到Session中
得到Session对象后,返回到4.1节ViewRootImpl构造器中赋给IWindowSession接口类型mWindowSession,再调用Session对象的addToDisplay
4.3 Session.java的addToDisplay方法
public int addToDisplay(IWindowwindow, int seq, WindowManager.LayoutParamsattrs,
int viewVisibility, int displayId, RectoutContentInsets, RectoutStableInsets,InputChanneloutInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outInputChannel);
}
mWindow:W对象,W实现了IWindow.Stub抽象类,继承了Binder类
mSeq:一个整型,每个按键事件的序号
mWindowAttributes:初始化语句有:
final WindowManager.LayoutParamsmWindowAttributes = new WindowManager.LayoutParams();
创建了一个布局参数对象
mAppVisible:确定mView即DecorView的可见性,默认为true,可见
mDisplay.getDisplayId():获得应用程序逻辑显示区域id号
mAttachInfo.mContentInsets:
final RectmContentInsets = new Rect();
一个矩形对象,作为窗口可见的矩形修饰
mAttachInfo.mStableInsets:
final RectmStableInsets = new Rect();
也是一个矩形对象,作为窗口固定的修饰
mInputChannel:待注册的管道对象
mService是WindowManagerService对象,在addToDisplay中调用WindowManagerService的addWindow方法
4.4 WindowManagerService.java的addWindow方法
与inputChannel相关部分:
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
4.4.1 InputChannel的openInputChannelPair
openInputChannelPair的实现在本地方法android_view_InputChannel_nativeOpenInputChannelPair中:
static jobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclassclazz, jstringnameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp serverChannel;
sp clientChannel;
status_tresult = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
声明服务端管道serverChannel、客户端管道clientChannel,作为openInputChannelPair的参数
4.4.1.1 InputTransprot.cpp的openInputChannelPair
status_tInputChannel::openInputChannelPair(const String8& name,
sp& outServerChannel, sp& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_tresult = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.string(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
int sockets[2];
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)
创建一个饱含2个元素的socket数组,socketpair创建一对socket对象,SOCK_SEQPACKET表示创建连续可靠的数据包连接,如果创建成功,返回0,如果返回-1,出错。
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
以这一对socket文件描述符、通道名称作为参数分别创建服务端管道对象outServerChannel和客户端管道对象clientChannelName
在InputChannel构造函数中,把管道名称传给mName,socket文件描述符保存到mFd中,设置mFd为非阻塞模式
4.4.1.2 android_view_InputChannel.cpp的android_view_InputChannel_nativeOpenInputChannelPair
jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
创建包含2个元素的数组,数组对象类型是gInputChannelClassInfo.clazz即InputChannel类型,元素初始化为空
jobjectserverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
用已创建的服务端通道serverChannel和客户端通道clientChannel作为参数分别创建本地通道对象NativeInputChannel,android_view_InputChannel_createInputChannel:
static jobjectandroid_view_InputChannel_createInputChannel(JNIEnv* env,
NativeInputChannel* nativeInputChannel) {
jobjectinputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor);
if (inputChannelObj) {
android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
}
return inputChannelObj;
}
创建Java层InputChannel类的对象保存到inputChannelObj中,如果成功,再调用android_view_InputChannel_setNativeInputChannel把刚才创建的NativeInputChannel对象指针赋给InputChannel对象中的变量Ptr,最后返回新创建的java层的InputChannel对象分别赋给serverChannelObj和clientChannelObj
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
用新创建的一对管道对象初始化数组channelPair,返回该数组赋给WindowManagerService中的inputChannels数组
小结:
makeInputChannelName创建了唯一的管道名称,openInputChannelPair以该名称作为参数创建一对管道InputChannel对象,存储在数组inputChannels中,inputChannels[0]作为服务端管道提供给inputdispatcher使用,用来发送按键事件;inputChannels[1]作为客户端管道提供给应用程序主线程使用,用来接收、消费按键事件。
4.4.2 InputManagerService的registerInputChannel
服务端管道inputChannels[0]创建好后,需要注册:
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
参数win.mInputWindowHandle是一个输入事件窗口的句柄,有多个作用:InputDispatcher在发送按键事件前会通过该句柄检查是否有获得焦点的窗口;如果有焦点的窗口,可以通过该句柄获得应用程序的InputChannel
registerInputChannel —-> nativeRegisterInputChannel
4.4.2.1 com_android_server_input_InputManagerService.cpp的nativeRegisterInputChannel函数
static void nativeRegisterInputChannel(JNIEnv* env, jclassclazz,
jlongptr, jobjectinputChannelObj, jobjectinputWindowHandleObj, jbooleanmonitor) {
NativeInputManager* im = reinterpret_cast(ptr);
sp inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
sp inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_tstatus = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
if (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}
先获得本地InputChannel对象和InputWindowHandle对象,再调用NativeInputManager的registerInputChannel函数:
status_tNativeInputManager::registerInputChannel(JNIEnv* env,
const sp& inputChannel,
const sp& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
通过InputManager对象获得对应的InputDispatcher对象,进而调用其registerInputChannel函数注册
status_tInputDispatcher::registerInputChannel(const sp& inputChannel,
const sp& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
toString(monitor));
#endif
{ // acquire lock
AutoMutex_l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
检查inputChannel是否注册,首次执行时,没有注册
sp connection = new Connection(inputChannel, inputWindowHandle, monitor);
通过创建的管道inputChannel以及窗口句柄inputWindowHandle创建Connection对象,Connection用来管理inputChannel
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
getFd获得服务端管道inputChannel上的socket文件描述符,然后以键值对的方式把connect加入到KeyedVector中,这就完成了服务端通道的注册。
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
然后把socket服务端加入到epoll兴趣列表中,mLooper属于InputDispatcherThread,不属于主线程,注意与4.5.3.1节的区别,4.5.3.1节是注册到主线程对应的epoll对象中。ALOOPER_EVENT_INPUT表示可读事件,意思是在服务端socket中读取数据,这样设置的目的在于,当客户端socket接受到事件处理完后会向服务端发送一个反馈信号,服务端接收此信号后会回调handleReceiveCallback函数,该函数会再一次执行startDispatchCycleLocked函数从outboundQueue中取出按键事件进行发送,循环处理。
addFd函数的具体执行过程与4.5.4节几乎一样,不再分析。
4.4.3 InputChannel.java的transferTo
在4.4.2节已经注册了服务端管道inputChannels[0],那么客户端管道inputChannels[1]如何注册?
inputChannels[1].transferTo(outInputChannel);
public void transferTo(InputChanneloutParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
nativeTransferTo(outParameter);
}
outInputChannel是InputChannel对象,inputChannels[1]也是刚刚创建的InputChannel对象,transferTo两头都是InputChannel对象,这又是怎么回事
transferTo的调用者是当前新创建客户端管道对象,参数outInputChannel是在ViewRootImpl中创建的,两者是不同的对象。
4.4.3.1 nativeTransferTo的本地实现
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobjectobj,
jobjectotherObj) {
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
"Other object already has a native input channel.");
return;
}
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, obj);
android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}
env:JNI接口对象
obj:本地方法所在对象的引用,就是inputChannels[1]客户端对象的引用
otherObj:在ViewRootImpl中创建的InputChannel对象
首先调用android_view_InputChannel_getNativeInputChannel函数
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
jobjectinputChannelObj) {
jlonglongPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
return reinterpret_cast(longPtr);
}
传过来的对象时otherObj,其变量mPtr在初始化时为空,没有额外赋值,所以longPtr为空,这句:
if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
不成立
又一次调用android_view_InputChannel_getNativeInputChannel函数,只不过第二个参数是obj即inputChannels[1],此时inputChannels[1]中的mPtr指针不为空,因此在3.4.1.2节中设置了mPtr为inputChannels[1]对应的NativeInputChannel地址,返回NativeInputChannel指针给nativeInputChannel变量,而后调用android_view_InputChannel_setNativeInputChannel函数把otherObj中的mPtr设为NativeInputChannel对象地址,这就表明ViewRootImpl中创建的InputChannel对象是客户端管道
读到此处可知,transferTo方法只是把ViewRootImpl中创建的InputChannel对象中的mPtr变量赋了值,该值就是客户端InputChannle对应的NativeInputChannel对象地址。
4.5 ViewRootImpl.java的setView方法
在4.4.2.1节,注册服务端管道InputChannel[0]时,创建了InputPublisher对象,用来发送消息到input channel,那么注册在客户端管道InputChannel[1]时,也会创建一个接收消息的对象,这就是InputConsumer,从管道中获取事件。客户端如何创建?还是在ViewRootImpl.java的setView方法中:
if (viewinstanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view是DecorView对象,DecorView实现了RootViewSurfaceTaker接口,因此,view也是RootViewSurfaceTaker接口类型,if语句成立,willYouTakeTheInputQueue实际返回一个NativeActivity对象,因为NativeActivity实现了InputQueue.Callback接口
InputQueue:应用程序通过InputQueue来获得输入设备事件
InputQueue.Callback:当InputQueue绑定、解除绑定与线程关系时的回调接口
创建了InputQueue对象,作为NativeActivity中回调方法onInputQueueCreated的参数;用InputChannel和Looper对象作为参数创建WindowInputEventReceiver对象并用mInputEventReceiver指向该对象。
InputQueue构造器中调用了本地方法nativeInit
mPtr = nativeInit(new WeakReference(this), Looper.myQueue());
第一个参数是InputQueue弱引用对象,第二个参数是主线程MessageQueue对象
4.5.1 android_view_InputQueue.cpp的nativeInit
static jlongnativeInit(JNIEnv* env, jobjectclazz, jobjectqueueWeak, jobjectjMsgQueue) {
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper());
if (!queue.get()) {
jniThrowRuntimeException(env, "InputQueue failed to initialize");
return 0;
}
queue->incStrong(&gInputQueueClassInfo);
return reinterpret_cast(queue.get());
}
android_os_MessageQueue_getMessageQueue获得C++层NativeMessageQueue对象,也是MessageQueue对象,getLooper()获得Looper对象,queueWeak是java层传递过来的InputQueue弱引用对象,createQueue源码:
InputQueue* InputQueue::createQueue(jobjectinputQueueObj, const sp& looper) {
int pipeFds[2];
if (pipe(pipeFds)) {
ALOGW("Could not create native input dispatching pipe: %s", strerror(errno));
return NULL;
}
fcntl(pipeFds[0], F_SETFL, O_NONBLOCK);
fcntl(pipeFds[1], F_SETFL, O_NONBLOCK);
return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]);
}
创建了一个管道pipeFds,设置管道读、写端为非阻塞模式
创建了c++层InputQueue对象,赋给queue变量。InputQueue的构造函数:
InputQueue::InputQueue(jobjectinputQueueObj, const sp& looper,
int dispatchReadFd, int dispatchWriteFd) :
mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd),
mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj);
}
分别把管道读端、写端文件描述符赋给mDispatchReadFd、mDispatchWriteFd;用JNI接口方法NewGlobalRef创建inputQueueObj本地全局引用赋给mInputQueueWeakGlobal
4.5.2 NativeActivity的onInputQueueCreated
public void onInputQueueCreated(InputQueuequeue) {
if (!mDestroyed) {
mCurInputQueue = queue;
onInputQueueCreatedNative(mNativeHandle, queue.getNativePtr());
}
}
把创建的InputQueue对象赋给mCurInputQueue,当创建好InputQueue后,既可回调onInputQueueCreated重写自己的代码,表明可以从InputQueue中获取事件
4.5.3 WindowInputEventReceiver的构造器
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());
public WindowInputEventReceiver(InputChannelinputChannel, Looperlooper) {
super(inputChannel, looper);
}
public InputEventReceiver(InputChannelinputChannel, Looperlooper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
调用WindowInputEventReceiver的构造方法创建了一个WindowInputEventReceiver对象,其构造方法没有实现,直接调用父类InputEventReceiver的构造方法,第一个参数就是客户端管道对象,其内部mPtr变量指向了客户端NativeInputChannel对象地址。
mReceiverPtr = nativeInit(new WeakReference(this),
inputChannel, mMessageQueue);
第一个参数this,实际类型是WindowInputEventReceiver类型,这个参数很重要,在5.3节会根据该类型决定调用哪个onInputEvent方法。
static jlongnativeInit(JNIEnv* env, jclassclazz, jobjectreceiverWeak,
jobjectinputChannelObj, jobjectmessageQueueObj) {
sp inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_tstatus = receiver->initialize();
if (status) {
String8message;
message.appendFormat("Failed to initialize input event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast(receiver.get());
}
NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
jobjectreceiverWeak, const sp& inputChannel,
const sp& messageQueue) :
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
mInputConsumer(inputChannel), mMessageQueue(messageQueue),
mBatchedInputEventPending(false), mFdEvents(0) {
}
用本地InputChannel, NativeMessageQueue及java层InputEventReceiver对象作为参数创建本地NativeInputEventReceiver对象并初始化,receiverWeak就是传递过来的WindowInputEventReceiver对象,赋给了mReceiverWeakGlobal变量。
创建好NativeInputEventReceiver对象后返回到java层InputEventReceiver中赋给mReceiverPtr变量,java层拥有这个变量就等于拥有了c++层NativeInputEventReceiver对象的指针
4.5.3.1 NativeInputEventReceiver的initialize函数
status_tNativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
InputConsumer获取客户端inputChannel管道上的socket文件描述符添加到epoll对象兴趣列表中,fd是socket的客户端。mMessageQueue是主线程中的消息队列,getLooper返回主线程Looper对象,因此,客户端socket文件描述符被注册到了主线程中epoll对象中。
4.5.4 Looper.cpp的addFd函数
int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback.get(), data);
#endif
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
{ // acquire lock
AutoMutex_l(mLock);
Requestrequest;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data;
struct epoll_eventeventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
ssize_trequestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.replaceValueAt(requestIndex, request);
}
} // release lock
return 1;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
addFd中第2个参数为0,第3个参数是ALOOPER_EVENT_INPUT,第4个参数this,是NativeInputEventReceiver对象,也是LooperCallback对象类型,第5个参数是NULL
第一个if语句不成立,把ident设置成POLL_CALLBACK,表示调用者有回调方法会被执行。
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
设置监控事件为EPOLLIN可读事件
Requestrequest;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data; (base\services\core\jni) 58730 2015/6/6
用传递过来的参数初始化Request结构体变量request
struct epoll_eventeventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
声明epoll机制事件结构体变量eventItem并初始化,设events为可读事件,注册的文件描述符为socket服务端fd
ssize_trequestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.replaceValueAt(requestIndex, request);
}
判断容器mRequests中是否有socket文件描述符fd,如果没有,就把fd注册到epoll对象mEpollFd中,否则,修改现有的epoll对象的兴趣列表中的fd,更新了eventItem。同时把调用者相关信息对象Request与fd作为一对映射关系保存到mRequests容器中,待执行epoll_wait时使用。
小结:
应用程序端管道inputChannels[1]注册在InputEventReceiver中,其socket对象注册到主线程epoll对象兴趣列表中,当socket对端即服务端inputChannels[0]上有数据写入后,应用程序便可从服务端socket对象上读取数据。