原來在Android2.3.5下能正常工做的TouchScreen功能,移植到Android 4.0就不能正常工做了。憑直覺,Android4.0確定有鬼。真是不看不知道,一看嚇一跳。在Android 4.0中,Event Input地位提升了,你看看,在Adroid2.3.5中,它在frameworks/base/libs/ui之下,在Android4.0中,它在 frameworks/base/services/input之下,看到沒有,它有了本身的地位,就像在Kernel中同樣,有本身門戶了。node
再看看代碼,變化也太大了,固然TouchScreen不能工做,首先天然會看接口部分代碼。首先看它是如何打開設備的,查看函數EventHub::openDeviceLocked,看看其代碼,大部分仍是很熟悉的,但仔細一看多了一個下面的東東:android
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);數據結構
因爲升級到Android4.0時,Kernel仍是2.6.35,並無進行升級。既然須要EVIOCGPROP,就就看看evdev.c中的 ioctl函數是否支持此功能。一看不支持,再看看Kernel3.0.8<這個Kernel版本與Android4.0是一夥的>,個人乖 乖,它已經支持了此功能,詳見evdev.c中函數evdev_do_ioctl,這個寫得2.6.35中的友好多了,分別處理:固定長度命令、單個可變 長度命令和多個可變長度命令。app
對於爲何個人TouchScreen在Android4.0不工做,答案顯而易見,我用的Kernel版本不對,固然移植到Android4.0對應的 Kernel(Kernel3.0.8)時,TouchScreen驅動自己也須要修改,由於input_dev變化也比較大,好比增長了propbit 字段,以供處理上面的ioctl時使用。異步
正是因爲遇到上面的問題,才促使本身對Event Input進行深刻了解。由於走馬觀花不是小弟的性格。ide
這個年代幹啥都有什麼經理,小弟之類的。好比去飯店吃飯,吃到小強了,老是會大吼一聲,經理,過來看看,而後談打折或賠償的問題。可見經理是不可缺乏的,要否則咱們找誰來維權啊!函數
前面談到的EventHub,這個一看就是一個作實事的,確定不是領導,哪它的領導是誰呢? 哪咱們就從如下幾方面來分析此問題:oop
1)每一個功能模塊是怎麼產生的?ui
2)讀取設備輸入流程?this
3)事件分發流程?
先介紹一下每一個模塊的工做職責:EventHub, InputReader, InputManager...
它是系統中全部事件的中央處理站。它管理全部系統中能夠識別的輸入設備的輸入事件,此外,當設備增長或刪除時,EventHub將產生相應的輸入事件給系統。
EventHub經過getEvents函數,給系統提供一個輸入事件流。它也支持查詢輸入設備當前的狀態(如哪些鍵當前被按下)。並且EventHub還跟蹤每一個輸入調入的能力,好比輸入設備的類別,輸入設備支持哪些按鍵。
InputReader從EventHub中讀取原始事件數據(RawEvent),並由各個InputMapper處理以後輸入對應的input listener.
InputReader擁有一個InputMapper集合。它作的大部分工做在InputReader線程中完成,可是InputReader能夠接受任意線程的查詢。爲了可管理性,InputReader使用一個簡單的Mutex來保護它的狀態。
InputReader擁有一個EventHub對象,但這個對象不是它建立的,而是在建立InputReader時做爲參數傳入的。
InputDispatcher負責把事件分發給輸入目標,其中的一些功能(如識別輸入目標)由獨立的policy對象控制。
InputManager是系統事件處理的核心,它雖然不作具體的事,但管理工做仍是要作的,好比接受咱們客戶的投訴和索賠要求,或者老闆的出所筒。
InputManager使用兩個線程:
1)InputReaderThread叫作"InputReader"線程,它負責讀取並預處理RawEvent,applies policy而且把消息送入DispatcherThead管理的隊列中。
2)InputDispatcherThread叫作"InputDispatcher"線程,它在隊列上等待新的輸入事件,而且異步地把這些事件分發給應用程序。
InputReaderThread類與InputDispatcherThread類不共享內部狀態,全部的通訊都是單向的,從 InputReaderThread到InputDispatcherThread。兩個類能夠經過InputDispatchPolicy進行交互。
InputManager類從不與Java交互,而InputDispatchPolicy負責執行全部與系統的外部交互,包括調用DVM業務。
1)在android_server_InputManager_nativeInit中建立NativeInputManager對象,並保存到gNativeInputManager中;
2)在建立NativeInputManager對象時,它會建立EventHub對象<且建立是其成員mNeedToScanDevices的值爲true>,而後把剛建立的EventHub對象做爲參數建立InputManager對象;
3)在建立InputManager對象時,建立InputReader對象,而後把它做爲參數建立InputReaderThread;建立InputDispatcher對象,而後把它做爲參數建立InputDispatcherThread對象;(注:以上兩個線程對象都有本身的threadLoop函數,它將在Thread::_threadLoop中被調用,這個Thread::_threadLoop是線程入口函數,線程在Thread::run中被真正地建立)
4.1)建立InputReader對象
4.1.1)把EventHub、readerPolicy<實質爲NativeInputManager對象>和建立的InputDispatcher對象做爲參數建立InputReader對象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
4.1.2)在建立InputReader時, 保存EventHub對象到mEventHub中,並建立QueuedInputListener對象並保存在mQueuedListener中
4.2)建立InputDispatcher對象
4.2.1)把傳入的參數dispatcherPolicy<實質爲 NativeInputManager對象>做爲參數建立InputDispatcher對象:mDispatcher = new InputDispatcher(dispatcherPolicy);
4.2.1)在建立InputDispatcher時,建立了一個looper對象:mLooper = new Looper(false);
1)在android_server_InputManager_nativeStart中調用InputManager::start,代碼以下:
result = gNativeInputManager->getInputManager()->start();
2)在InputManager::start中,調用mDispatcherThread->run和mReaderThread->run,代碼以下:
result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
3)在上面的Thread::run中,調用createThreadEtc函數,並以Thread::_threadLoop做爲入口函數,以上面的mDispatcherThread或mReaderThread做爲userdata建立線程
4)至此InputReader線程和InputDispatcher線程都已經工做,詳細信息見Thread::_threadLoop,在此函數中它將調用mDispatcherThread或mReaderThread的threadLoop函數來作真正的事
5.1)mReaderThread->threadLoop
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
5.2)mDispatcherThread->threadLoop
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
從EventHub::getEvents讀取的事件數據結構以下:
[cpp] view plaincopy
struct RawEvent {
nsecs_t when; //事件發生的時間
int32_t deviceId; //產生此事件的設備,好比發送FINISHED_DEVICE_SCAN,不須要填此項
int32_t type; //事件類型(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN)
int32_t scanCode;
int32_t keyCode;
int32_t value;
uint32_t flags;
};
讀取事件時的調用流程爲:
Thread::_threadLoop->
InputReaderThread::threadLoop->
InputReader::loopOnce->
EventHub::getEvents->
在EventHub::getEvents中,當mNeedToScanDevices爲true時<當建立EventHub對象時,它就爲true>,它將從/dev/input目錄下查找全部設備,並進行打開,獲取其相關屬性,最後加入mDevices列表中。
EventHub::scanDevicesLocked->
EventHub::scanDirLocked("/dev/input")->
EventHub::openDeviceLocked
打開事件輸入設備,在用戶態調用open,則在kernel態中調用evdev_open函數,evdev_open處理流程以下:
1)首先從參數inode中獲取在evdev_table中的索引,從而獲取對應的evdev對象
2)建立evdev_client對象,建立此對象時同時爲其buffer成員分配對應的內存
3)把新建立evdev_client對象添加到client_list鏈表中
4)把client保存在file的private_data中
5)調用evdev_open_device->input_open_device->input_dev.open函數打開設備。
要說EventHub::getEvents如何獲取輸入事件,不得不先說說它的幾個相關的成員變量:
1)mPendingEventCount:調用epoll_wait時的返回值,固然若是沒有事件,則其值爲0;
2)mPendingEventIndex:當前須要處理的事件索引
3)mEpollFd:epoll實例,在EventHub::EventHub中初始化此例,全部輸入事件經過epoll_wait來獲取,每個事件 的數據結構爲:struct epoll_event,爲了搞明白如何讀取輸入事件的原理,不得不對epoll相關的東東搞個清清楚楚,明明白白,見epoll kernel實現原理。注:epoll_event只代表某個設備上有事件,並不包含事件內容,具體事件內容須要經過read來讀取。
struct epoll_event定義以下:
[cpp] view plaincopy
typedef union epoll_data
{
void *ptr;
int fd;
unsigned int u32;
unsigned long long u64;
} epoll_data_t;
struct epoll_event
{
unsigned int events;
epoll_data_t data;
};
每一個設備被建立(在函數EventHub::openDeviceLocked中)時,都會向epoll註冊,代碼以下:
[cpp] view plaincopy
// Register with epoll.
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = deviceId;
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
LOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
在調用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)以後,讀到的epoll_event事件保存在mPendingEventItems,總共的事件數保存在 mPendingEventCount,固然,在調用epoll_event以前,mPendingEventIndex被清0,直正的事件處理在下面的 代碼中。
[cpp] view plaincopy
// 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 {
LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
LOGV("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 {
LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0) {
LOGW("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.
LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
if (errno != EAGAIN && errno != EINTR) {
LOGW("could not get event (errno=%d)", errno);
}
} else if ((readSize % sizeof(struct input_event)) != 0) {
LOGE("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++) {
const struct input_event& iev = readBuffer[i];
LOGV("%s got: t0=%d, t1=%d, 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);
#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;
LOGV("event time %lld, now %lld", event->when, now);
#else
event->when = now;
#endif
event->deviceId = deviceId;
event->type = iev.type;
event->scanCode = iev.code;
event->value = iev.value;
event->keyCode = AKEYCODE_UNKNOWN;
event->flags = 0;
if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
&event->keyCode, &event->flags);
LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
iev.code, event->keyCode, event->flags, err);
}
event += 1;
}
capacity -= count;
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 {
LOGW("Received unexpected epoll event 0x%08x for device %s.",
eventItem.events, device->identifier.name.string());
}
}
epoll_wait只是告訴咱們Device已經有事件了,讓咱們去讀,真正讀取設備輸入事件的代碼如上,其流程以下:
1)根據eventItem.data.u32獲取設備索引,從而獲取對應的Device
2)從device->fd中讀取input_event事件。 read(device->fd, readBuffer, sizeof(struct input_event) * capacity);這些input_event是由各個註冊的input_device報告給input子系統的。具體讀入流程參見Input Core和evdev基本知識 - Kernel3.0.8
在4.2中,首先經過epoll_wait查看哪些設備有事件,而後經過read從有事件的設備中讀取事件,如今事件已經讀取到用戶態,且數據結構爲 input_event,保存在EventHub::getEvents的readBuffer中。下面就看看這些事件下一步的東家是誰?
1)首先把input_event的信息填入RawEvent中,其相關代碼以下:
[plain] view plaincopy
#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;
LOGV("event time %lld, now %lld", event->when, now);
#else
event->when = now;
#endif
event->deviceId = deviceId;
event->type = iev.type;
event->scanCode = iev.code;
event->value = iev.value;
event->keyCode = AKEYCODE_UNKNOWN;
event->flags = 0;
if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
&event->keyCode, &event->flags);
LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
iev.code, event->keyCode, event->flags, err);
}
2)若是是input_event的類型爲EV_KEY,則須要調用 device->keyMap.keyLayoutMap->mapKey函數把iput_event.code映射爲 RawEvent.keyCode。相關數據結構關係以下圖所示:
至此,EventHub::getEvents讀取事件的任務已經完成,下面看看這些RawEvent的命運如何呢?
爲此,先溫習一下讀取事件時的調用流程爲:
Thread::_threadLoop->
InputReaderThread::threadLoop->
InputReader::loopOnce->
EventHub::getEvents->
在InputReader::loopOnce中,當調用EventHub->getEvents獲取到RawEvent以後,調用 InputReader::processEventsLocked來處理這些事件,而後調用mQueuedListener->flush()把 這些隊列中的事件發送到Listener。
在InputReader::processEventsLocked主要分兩步處理:
1)處理來自於事件驅動設備的事件(processEventsForDeviceLocked)
2)處理設備增長、刪除和修改事件
按照程序執行流程,應該是先有設備,而後纔會有設備事件,因此先分析設備增長。 其代碼以下:
[plain] view plaincopy
<span style="font-size:10px;">void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count)
{
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
//處理來自於事件驅動設備的事件
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
//處理來自於同一個事件驅動設備的1個或多個事件
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
}
else
{
//處理增長或刪除事件驅動設備的事件,在EventHub::getEvents中產生,
//不是由事件驅動設備產生的。
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
LOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}</span>
它處理其中的 EventHubInterface::DEVICE_ADDED, EventHubInterface:: DEVICE_REMOVED和EventHubInterface::FINISHED_DEVICE_SCAN事件,即與Device相關的事件,這 些事件是在EventHub::getEvents中產生的,並非Kernel態的事件輸入設備產生的。
下面分析它如何處理EventHubInterface::DEVICE_ADDED事件。查看其它代碼,它是調用InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId)來處理此事件。
在InputReader::addDeviceLocked中的調用流程:
1)先根據mContext, deviceId, name, classes建立一個InputDevice對象,它用於表示單個輸入設備的狀態。其中的classes爲對應Device的classes成員,它用於表示設備類型,其定義以下:
[plain] view plaincopy
/*
* Input device classes.
*/
enum {
/* The input device is a keyboard or has buttons. */
INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
INPUT_DEVICE_CLASS_TOUCH = 0x00000004,
/* The input device is a cursor device such as a trackball or mouse. */
INPUT_DEVICE_CLASS_CURSOR = 0x00000008,
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
INPUT_DEVICE_CLASS_DPAD = 0x00000020,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040,
/* The input device has switches. */
INPUT_DEVICE_CLASS_SWITCH = 0x00000080,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100,
/* The input device is external (not built-in). */
INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000,
}
建立InputDevice對象以後, 對於多點觸摸設備(class爲INPUT_DEVICE_CLASS_TOUCH_MT),建立MultiTouchInputMapper對象並增長到InputDevice的mMappers向量列表中。
對於單點觸摸設備(class爲INPUT_DEVICE_CLASS_TOUCH),建立SingleTouchInputMapper對象並增長到InputDevice的mMappers向量列表中。相關代碼以下:
[plain] view plaincopy
InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
const String8& name, uint32_t classes) {
InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);
....
if (keyboardSource != 0) {
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
}
// Cursor-like devices.
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper(device));
}
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
// Joystick-like devices.
if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
device->addMapper(new JoystickInputMapper(device));
}
return device;
}
總之,它調用createDeviceLocked建立一個InputDevice設備,並根據class類別建立對應的事件轉換器 (InputMapper),而後把這些新那建的InputMapper增長到InputDevice::mMappers中。InputMapper關 系以下圖所示:
2)調用InputDevice::configure配置此InputDevice
3)調用InputDevice::reset重置此InputDevice
4)把新建的InputDevice增長到InputReader::mDevices中。
InputReader::processEventsLocked設備增長、刪除處理總結:
它負責處理Device 增長、刪除事件。增長事件的流程爲:爲一個新增的Device建立一個InputDevice,並增長到InputReader::mDevices中; 根據新增長設備的class類別,建立對應的消息轉換器(InputMapper),而後此消息轉換器加入InputDevice::mMappers 中。消息轉換器負責把讀取的RawEvent轉換成特定的事件,以供應用程序使用。
EventHub與InputReader各自管理功能:
1)EventHub管理一堆Device,每個Device與Kernel中一個事件輸入設備對應
2)InputReader管理一堆InputDevice,每個InputDevice與EventHub中的Device對應
3)InputDevice管理一些與之相關的InputMapper,每個InputMapper與一個特定的應用事件相對應,如:SingleTouchInputMapper。
下面的分析處理以單點觸摸爲例,對於單點觸摸Touch Down時,它將報告如下事件:
代碼:
input_report_abs(myInputDev, ABS_X, event->x);
input_report_abs(myInputDev, ABS_Y, event->y);
產生的事件:*type, code, value
EV_ABS,ABS_X,event->x
EV_ABS,ABS_Y,event->y
代碼:
input_report_key(myInputDev, BTN_TOUCH, 1);
產生的事件:*type, code, value
EV_KEY, BTN_TOUCH, 1
代碼:
input_sync(myInputDev);
它調用input_event(dev, EV_SYN, SYN_REPORT, 0);
產生的事件:*type, code, value
EV_SYN, SYN_REPORT, 0
它負責處理來自於同一個設備且在mEventBuffer中連續的多個事件,其函數原型以下:
[plain] view plaincopy
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
LOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//LOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count);
}
它其實很簡單,根據輸入的deviceId找到對應的InputDevice,而後調用InputDevice::process以對設備輸入事件進行處理。InputDevice::process主要源碼以下:
[plain] view plaincopy
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++)
{
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
從上面的代碼中能夠看出,在InputDevice::process中,對於傳入的每個RawEvent,依次調用InputDevice中的每個 InputMapper來進行處理。前面提到過,InputDevice包含一組處理對應設備事件InputMapper,如今這些 InputMapper開始幹活了。
下面以處理一個單點觸摸事件設備的事件爲例,進行分析,其它的處理流程相似。對於mapper->process須要查看 InputReader::createDeviceLocked中建立的具體的InputMapper的process函數。下面就看看 SingleTouchInputMapper的process是如何處理的,其代碼以下:
[plain] view plaincopy
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
TouchInputMapper::process(rawEvent);
mSingleTouchMotionAccumulator.process(rawEvent);
}
1)TouchInputMapper::process
因而可知,它將首先調用TouchInputMaaper::process處理此事件,其處理代碼以下:
[plain] view plaincopy
void TouchInputMapper::process(const RawEvent* rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {
sync(rawEvent->when);
}
}
1.1) mCursorButtonAccumulator.process(rawEvent)
記錄mouse或touch pad按鍵狀態,記錄rawEvent->type爲EV_KEY,且rawEvent->scanCode爲BTN_LEFT、BTN_RIGHT、BTN_MIDDLE、BTN_BACK、BTN_SIDE、BTN_FORWARD、BTN_EXTRA、BTN_TASK的事件。
1.2) mCursorScrollAccumulator.process(rawEvent)
記錄cursor scrolling motions,記錄rawEvent->type爲EV_REL,且rawEvent->scanCode爲REL_WHEEL、REL_HWHEEL的事件。
1.3) mTouchButtonAccumulator.process(rawEvent)
記錄touch, stylus and tool buttons狀態,記錄rawEvent->type爲EV_KEY,且rawEvent->scanCode爲BTN_TOUCH、 BTN_STYLUS、BTN_STYLUS二、BTN_TOOL_FINGER、BTN_TOOL_PEN、BTN_TOOL_RUBBER、 BTN_TOOL_BRUSH、BTN_TOOL_PENCIL、BTN_TOOL_AIRBRUSH、BTN_TOOL_MOUSE、 BTN_TOOL_LENS、BTN_TOOL_DOUBLETAP、BTN_TOOL_TRIPLETAP、BTN_TOOL_QUADTAP的事件。
看到了吧,咱們的BTN_TOUCH在這兒被處理了,且其value被保存在mBtnTouch成員變量中。
1.4) sync(rawEvent->when)
處理EV_SYN:SYN_REPORT,咱們的EV_SYN就在這兒被處理了,固然它是Touch Down時,所發事件的最後一個事件。這兒纔是處理的重點。
TouchInputMapper::sync將調用SingleTouchInputMapper::syncTouch函數。
a)SingleTouchInputMapper::syncTouch
把mCurrentRawPointerData中的ABS_X和ABS_Y的值保存在TouchInputMapper::mCurrentRawPointerData->pointers中。
單點觸摸的syncTouch一次處理一個RawEvent,在pointers中只有一個值;而多點觸摸的syncTouch一次處理多個RawEvent,在pointers中有多個值,最多16個。
b)TouchInputMapper::cookPointerData
根據TouchInputMapper::mCurrentRawPointerData->pointers中的數據,經過計算,最後生成 TouchInputMapper::mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.pointerProperties 和mCurrentCookedPointerData.idToIndex的數據。把Raw進行cook,以後生成了cooked數據。
c)TouchInputMapper::dispatchHoverExit
d)TouchInputMapper::dispatchTouches
d.a)它調用dispatchMotion
d.b)在dispatchMotion中,根據cooked數據建立NotifyMotionArg對象,它描述了一個移動事件
d.c)調用TouchInputMapper::getListener()->notifyMotion(&args)
TouchInputMapper::getListener()調用mContext->getListener(),此mContext爲InputReader::mContext,因此其getListener()返回的則爲InputReader::mQueuedListener,則最後調用QueuedInputListener::notifyMotion
補充1) InputReader::mContext在構造時用本身的指針初始化了mContext,從而mContext::mReader則爲此InputReader實例。
補充2) 在InputReader::createDeviceLocked中建立InputDevice時,把本身的mContext做爲參數傳入,從而把它保 存在InputDevice::mContext中;在建立InputMapper時,以InputDevice做爲參數,且InputMapper把它 保存在mDevice中,而後從把InputDevice中的mContext也保存在InputMapper的mContext中。
d.d)把傳遞過來的NotifyMotionArg參數複製一份,而後加入QueuedInputListener::mArgsQueue例表中。
e)TouchInputMapper::dispatchHoverEnterAndMove
2)mSingleTouchMotionAccumulator.process
記錄ABS相關的值,記錄rawEvent->type爲EV_ABS,且rawEvent->scanCode爲ABS_X、ABS_Y、ABS_PRESSURE、ABS_TOOL_WIDTH、ABS_DISTANCE、ABS_TILT_X、ABS_TILT_Y的事件。咱們發的ABS_X和ABS_Y在這兒被處理了。
事件處理相關數據結構以下圖所示:
先溫習一下,至此的消息結構變化流程:
processEventsLocked已經把來自於事件設備的事件煮熟以後放入到各類NotifyArgs(如NotifyMotionArgs)之 中,而後把這些各類NotifyArgs加入InputReader::mQueuedListener::mArgsQueue鏈表中。本Flush函 數就是要把mArgsQueue中的全部NotifyArgs進行處理。爲描述方便,先看看其代碼:
[plain] view plaincopy
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
看到了吧,確實很簡單,調用鏈表中每一個NotifyArgs的notify函數,且有一個有意思的參數 mInnerListener,這個參數在前面多 次提到過,它是在建立mQueuedListener時提供的,它其實就是InputManager中的mDispatcher,前面一直在 InputReader中打轉轉,如今終於看到InputDispatcher登場了,說明事件很快就能夠謝幕了。
再向下看一下吧,這麼多類NotifyArgs,爲描述方便,下面以NotifyMotionArgs爲例,其代碼爲:
[cpp] view plaincopy
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
下面就看看InputDispatcher(mDispatcher)的notifyMotion函數作了些什麼。這個InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不簡單了。
在InputDispatcher::notifyMotion中,
1)根據NotifyMotionArgs提供的信息,構造一個MotionEvent,再調用 mPolicy->filterInputEvent看是否須要丟棄此事件,若是須要丟棄則立刻返加。其中mPolicy爲 NativeInputManager實例,在構造InputDispatcher時提供的參數。
2)對於AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,則直接根據NotifyMotionArgs提供的信息,構造一個MotionEntry。
3)調用InputDispatcher::enqueueInboundEventLocked把新構造的MotionEntry添加到InputDispatcher::mInboundQueue中,並返回是否須要喚醒mLooper<向pipe中寫入數據>的標識。
以上操做都是在InputReader線程中完成的,如今應該InputDispatcher線程開始工做了。
InputDispatcherThread主循環以下:
Thread::_threadLoop->
InputDispatcherThread::threadLoop->
mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->
dispatchOnceInnerLocked then
mLooper->pollOnce
下面先看看簡單的mLooper->pollOnce
其功能爲等待超時或被pipe喚醒(InputReader線程調用InputDispatcher::notifyMotion時, InputDispatcher::notifyMotion根據狀況調用mLooper->wake)。
其調用流程以下:
mLooper->pollOnce(int timeoutMillis)->
Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->
1)從mInboundQueue從中依次取出EventEntry<MotionEntry的基類>,
2)調用InputDispatcher::dispatchMotionLocked處理此MotionEntry
3)調用InputDispatcher::dispatchEventToCurrentInputTargetsLocked
對於InputDispatcher::mCurrentInputTargets中的每個InputTarget,並獲取對應的 Connection,調用InputDispatcher::prepareDispatchCycleLocked,
其相關代碼以下:
[cpp] view plaincopy
<span style="font-size:10px;"> for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
resumeWithAppendedMotionSample);
} else {
#if DEBUG_FOCUS
LOGD("Dropping event delivery to target with channel '%s' because it "
"is no longer registered with the input dispatcher.",
inputTarget.inputChannel->getName().string());
#endif
}
}</span>
4)InputDispatcher::prepareDispatchCycleLocked
4.1)調用enqueueDispatchEntryLocked建立DispatchEntry對象,並把它增長到Connection::outboundQueue隊列中。
4.2)調用activateConnectionLocked把當前Connection增長到InputDispatcher::mActiveConnections鏈表中
4.3)調用InputDispatcher::startDispatchCycleLocked,接着它調用Connection::inputPublisher.publishMotionEvent來發布事件到ashmem buffer中,調用Connection::inputPublisher.sendDispatchSignal發送一個dispatch信號到InputConsumer通知它有一個新的消息到了,快來消費吧! 關於消費者如何註冊和如何消息的流程在下一個專題中再寫。本文到此結束!!!