Android输入事件从读取到分发二:谁在循环监听事件的到来
来源:互联网 发布:samba python install 编辑:程序博客网 时间:2024/06/03 17:18
通过上一节初步阅读代码,已经找到了读写/dev/input/设备文件节点的位置。但是最后,我觉得应该有一个线程,一直循环监听这些输入设备,有事件的时候就去处理,没有事件的时候就睡眠,等待事件的到来。那么,这部分的代码是怎么样的呢?
上一节只是为了定位android系统在什么地方监听输入设备,所以很多地方没有仔细分析,这一节,带着文章开头提出的问题,再一次分析源码,而我们的入口,任然是系统启动后,注册服务的SystemServer.java文件。
还是注册InputManagerService的这部分代码,上一节我们只是分析了InputManagerService的构造函数,没有分析最后调用的这个start方法,那这一次,就从这个start方法入手,再一次追踪android输入系统在系统开机后的一系列动作,从而明白输入系统是怎么一步步运行起来的。start函数的代码如下:
- Slog.i(TAG, "Input Manager");
- inputManager = new InputManagerService(context);
- Slog.i(TAG, "Window Manager");
- wm = WindowManagerService.main(context, inputManager,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
- !mFirstBoot, mOnlyCore);
- ServiceManager.addService(Context.WINDOW_SERVICE, wm);
- ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
- mActivityManagerService.setWindowManager(wm);
- inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
- inputManager.start();
start方法一开始就调用了nativeStart方法,记得上一节分析InputServiceManager的构造函数时,它调用了nativeInit()方法,从而展开了一系列输入事件管理的初始化动作。那么这里的nativeStart是不是就是在构造函数中调用nativeInit后,正式启动输入事件的管理框架的呢?感觉好像是的。nativeStart应该和nativeInit在同一个文件中:base/services/core/jni/com_android_server_input_InputManagerService.cpp,源码如下:
- public void start() {
- Slog.i(TAG, "Starting input manager");
- nativeStart(mPtr);
- // Add ourself to the Watchdog monitors.
- Watchdog.getInstance().addMonitor(this);
- registerPointerSpeedSettingObserver();
- registerShowTouchesSettingObserver();
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- updatePointerSpeedFromSettings();
- updateShowTouchesFromSettings();
- }
- }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
- updatePointerSpeedFromSettings();
- updateShowTouchesFromSettings();
- }
这里调用了NativeInputManager的getInputManager方法,这个方法很简单:
- static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- status_t result = im->getInputManager()->start();
- if (result) {
- jniThrowRuntimeException(env, "Input manager could not be started.");
- }
- }
- inline sp<InputManager> getInputManager() const { return mInputManager; }
它是一个内联方法,只是简单返回mInputManager变量,mInputManager是什么呢?可以看到它是一个InputManager的实例。所以,在nativeStart方法中,调用的start()是InputManager的start方法,这个方法源码如下:
- mInputManager = new InputManager(eventHub, this, this);
这里只做了两件事情,分别启动一个线程,先看第一个线程:
- status_t InputManager::start() {
- status_t result = 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;
- }
从名字上看,是事件的分发线程,目前我关注的不是分发,而是事件的读取,所以这里先做个标记,之后研究分发事件的时候,再认真看这里,目前,这里略过。那么第二个线程是什么呢?
- mDispatcherThread = new InputDispatcherThread(mDispatcher);
- mReaderThread = new InputReaderThread(mReader);
从名字上看,它是一个读取事件的线程,好像关键点已经要到来了。nativeStart方法中调用了这个线程的run方法,这会导致thread_loop方法杯调用。InputReaderThread类定义在InputReader.h中,实现在InputRead.cpp中,源码如下:
InputReaderThreader的构造函数和析构函数都为空,而thread_loop中也只是调用了mReader->loopOnce()方法。注意,这里是C++的线程定义方式,C++线程内建的函数在thread_loop函数返回true后,会不断重复调用thread_loop函数。所以这里可以看做一个死循环。但这里并不是轮询机制,因为loopOnce函数可能会休眠。这里很可能就是我们找的事件监听的循环线程,它不断重复的调用LoogOnce来监听输入事件。所以,现在看一下loopOnce()函数,这是个mReader指向的函数,mReader是一个InputReader的实例,所以这里就看一下InputReader类中的loopOnce方法:
- // --- InputReaderThread ---
- InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
- }
- InputReaderThread::~InputReaderThread() {
- }
- bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
- }
这个函数就比较长了,主要也就两个代码块,中间夹了一个函数。这个函数有点特别,因为它是EventHub下的一个函数,而EventHub类,就是上节分析出来的,真正直接监听/dev/input/设备节点的类。这个函数就是:mEventHub->getEvents,源码当然在EventHub中:
- void InputReader::loopOnce() {
- int32_t oldGeneration;
- int32_t timeoutMillis;
- bool inputDevicesChanged = false;
- Vector<InputDeviceInfo> inputDevices;
- { // acquire lock
- AutoMutex _l(mLock);
- oldGeneration = mGeneration;
- timeoutMillis = -1;
- uint32_t changes = mConfigurationChangesToRefresh;
- if (changes) {
- mConfigurationChangesToRefresh = 0;
- timeoutMillis = 0;
- refreshConfigurationLocked(changes);
- } else if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
- }
- } // release lock
- size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
- { // acquire lock
- AutoMutex _l(mLock);
- mReaderIsAliveCondition.broadcast();
- if (count) {
- processEventsLocked(mEventBuffer, count);
- }
- if (mNextTimeout != LLONG_MAX) {
- nsecs_t now = 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();
- }
这个函数很大,很长,挺复杂的,但它的确就是直接读写/dev/input/设备文件节点的函数:
- size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
- ALOG_ASSERT(bufferSize >= 1);
- AutoMutex _l(mLock);
- struct input_event readBuffer[bufferSize];
- RawEvent* event = buffer;
- size_t capacity = bufferSize;
- bool awoken = false;
- for (;;) {
- nsecs_t now = 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;
- delete device;
- 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_t nRead;
- 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_t deviceIndex = 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_t readSize = 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_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
- size_t count = 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_t time = 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;
- }
- int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
这里监听有没有事件到来,没有就休眠,有的话就返回......
至此,文章一开始提出的问题就得到了解决,问题是:”哪个线程一直监听着事件的到来,是怎么监听的?“
答案显然是:InputReaderThread线程一直监听着事件的到来,最终使用epoll_wait监听这些/dev/input/下的文件节点的文件描述符。
得到了这个答案后,再结合上一节得到的结论,事件的输入监听部分基本框架弄明白了,那下一步,就是继续分析事件的分发,毕竟事件最终要分发道view或着activity这些ui组件上。
阅读全文
0 0
- Android输入事件从读取到分发二:谁在循环监听事件的到来
- Android输入事件从读取到分发二:谁在循环监听事件的到来
- Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程
- Android输入事件从读取到分发五:事件分发前的拦截过程
- Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程
- Android输入事件从读取到分发五:事件分发前的拦截过程
- Android输入事件从读取到分发一:是谁在读取输入事件
- Android输入事件从读取到分发一:是谁在读取输入事件
- Android输入事件从读取到分发四:InputDispatcherThread发送事件到View结构树的过程
- Android输入事件从读取到分发四:InputDispatcherThread发送事件到View结构树的过程
- Android的事件分发(二)
- Android事件的分发(二)
- 二、Android事件分发
- Android事件分发二
- Android事件分发<二>
- Android的输入事件分发机制笔记
- android 的事件分发从源码分析
- 从jQuery的缓存到事件监听
- jQuery一行中元素的分类选择
- RESTful API 设计指南
- (009) java后台开发之堆和栈的区别
- IDEA下建立Maven项目
- 回文质数
- Android输入事件从读取到分发二:谁在循环监听事件的到来
- logstash将采取kafka的数据到elasticSearch配置
- faster-rcnn 之 RPN网络的结构解析
- echarts模拟从数据库异步加载数据
- 第八周项目二(3) 顺序串算法测试-串内元素的删除
- N-Queens II
- bzoj1005 小明的烦恼
- oracle创建存储过程并调用
- Android零基础入门第79节:Intent 属性详解(上)