安卓input子系统是通过事件管道 到达系统的各个层。
在最低层,物理输入设备产生了描述的状态变化的 信号,如按键 和 触摸接触点。然后进行编码,并以某种方式发送这些信号,例如通过USB HID报告或I2C总线。这些信号被linux Kernel的驱动 解码。驱动会把这些信号翻译成 标准的事件类型 和 代码。Linux内核 在linux/input.h 中定义了一套标准的事件类型和代码。
android的 EventHub 组件 通过打开dev/input 中的设备文件的方式从linux kernel读取这些输入事件,然后android的 InputReader 组件 会把这些linux输入事件翻译成一个android输入事件流.。
在翻译过程中涉及到device configuration, keyboard layout files, and various mapping tables.三个配置文件。最后InputReader 会把这些翻译好的事件发送给合适的应用程序窗口。
总的来看android4.0的input子系统可以分为两部分,安卓部分和linuxkernel部分。
这个文档我们只分析Android部分,Kernel会在另一篇文章里说明。
Android开机时候会创建一个进程来启动各种服务,其中有一个会启动input的服务,这是android input的运行时候的核心,下面我们具体来看。
一、InputDispatcher 和 InputReader线程 的创建过程
这两个线程是Input服务的核心线程,当android开机以后会首先建立这两个线程,然后进一步的完成整个input模块的初始化。InputReader负责input事件的读取,InputDispatcherThread负责将input事件的分发给上层app。
下面我们从看这两个线程的创建过程, android开机过程 中我们知道开机后系统会创建ServerThread这个线程来启动很多服务, 这两个线程也是从这里启动的,Android开始启动服务函数在
\android\frameworks\base\services\java\com\android\server\SystemServer.java中
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
private static final String ENCRYPTED_STATE = "1";
ContentResolver mContentResolver;
void reportWtf(String msg, Throwable e) {
Slog.w(TAG, "***********************************************");
Log.wtf(TAG, "BOOT FAILURE " + msg, e);
}
@Override
public void run() {
。。。。。。
。。。。。。
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ActivityManagerService.self().setWindowManager(wm);
。。。。。
。。。。。。
}
这里执行WindowManagerService.main()函数创建Window Manager的服务我们来看WindowManagerService.main()
具体代码在\android\frameworks\base\services\java\com\android\server\wm\WindowManagerService.java :
public static WindowManagerService main(Context context,
PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs) {
WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs);
thr.start();
synchronized (thr) {
while (thr.mService == null) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
return thr.mService;
}
}
函数里调用new WMThread(context, pm, haveInputMethods, allowBootMsgs);
我们看WMThread定义在\android\frameworks\base\services\java\com\android\server\wm\WindowManagerService.java
static class WMThread extends Thread {
WindowManagerService mService;
private final Context mContext;
private final PowerManagerService mPM;
private final boolean mHaveInputMethods;
private final boolean mAllowBootMessages;
public WMThread(Context context, PowerManagerService pm,
boolean haveInputMethods, boolean allowBootMsgs) {
。。。。。。。。
。。。。。。。。
WindowManagerService s = new WindowManagerService(mContext, mPM,
mHaveInputMethods, mAllowBootMessages);
。。。。
。。。。。。
}
这个函数调用了new WindowManagerService(mContext, mPM, mHaveInputMethods, mAllowBootMessages)
我们继续看这个函数 也在这个文件中代码如下:
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods, boolean showBootMsgs) {
。。。。。。。。。。。
。。。。。。。。。。。
mInputManager = new InputManager(context, this);
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
synchronized (thr) {
while (!thr.mRunning) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
mInputManager.start();
}
可以看到这里调用mInputManager = new InputManager(context, this);来创建了Java层的input管理器,
下面继续看InputManager()在\android\frameworks\base\services\java\com\android\server\wm\InputManager.java中
public InputManager(Context context, WindowManagerService windowManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
this.mCallbacks = new Callbacks();
Looper looper = windowManagerService.mH.getLooper();
Slog.i(TAG, "Initializing input manager");
nativeInit(mContext, mCallbacks, looper.getQueue());
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
}
注意看这里的nativeInit(mContext, mCallbacks, looper.getQueue());这句,
这个函数定义private static native void nativeInit(Context context, Callbacks callbacks,
MessageQueue messageQueue);
说明这个函数是native层的一个函数,我们继续来翻这个函数的定义,在android\frameworks\base\services\jni
\com_android_server_InputManager.cpp中这是一个c++函数,看来是调用一个动态链接库。具体代码:
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Landroid/content/Context;"
"Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
(void*) android_server_InputManager_nativeInit },
。。。。。。。。。
}//定义一个 JNINativeMethod然后在下面函数中注册
int register_android_server_InputManager(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "com/android/server/wm/InputManager",
gInputManagerMethods, NELEM(gInputManagerMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
Android的native层与java层都是这么调用的。我们继续看看
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {
if (gNativeInputManager == NULL) {
sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);
} else {
LOGE("Input manager already initialized.");
jniThrowRuntimeException(env, "Input manager already initialized.");
}
}
这里调用了gNativeInputManager = new NativeInputManager(contextObj, callbacksObj,looper);继续看这个函
数 也在当前文件下:
NativeInputManager::NativeInputManager(jobject contextObj,
jobject callbacksObj, const sp<Looper>& looper) :
mLooper(looper) {
。。。。。。。。。
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
这里sp<EventHub> eventHub = new EventHub();EventHub是与kernel打交道的管理器 它处理了所有kernel传上来
的input事件并转换处理发送给framework层使用。还有mInputManager = new InputManager(eventHub,this,this);
InputManager负责一些input的系统的初始化和正常工作时候一些管理工作。
看完代码发现eventHub的运行是由InputManager调用的,因此我们先从InputManager看起:
先看InputManager在\android40\frameworks\base\services\input\ InputManager.cpp
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
注意这两个函数的参数,参数mReade定义sp<InputReaderInterface> mReader;这是定义类指针的一种方式,知道这
两个是类的指针就可以了。他们的赋值在\android\frameworks\base\services\input\InputManager.cpp
namespace android {
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
这里InputReaderThread 和InputDispatcherThread 两个线程就创建完成了,我们还需要对他们实现代码进行一小
下分析InputReader和InputDispatcher定义代码分别在\android\frameworks\base\services\input\InputReader.cpp
\android\frameworks\base\services\input\InputDispatcher.cpp 中
他们的代码为
InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
Thread(/*canCallJava*/ true), mReader(reader) {
}
InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}
定义竟然是空的,仅仅是传进去一个类的指针而已。那么这两个线程运行什么呢?这个问题我百度了很多资料终于
找到了。\android\frameworks\base\services\java\com\android\server\wm\WindowManagerService.java中
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods, boolean showBootMsgs) {
。。。。。。。。。。。
。。。。。。。。。。。
mInputManager = new InputManager(context, this);
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
thr.start();
synchronized (thr) {
while (!thr.mRunning) {
try {
thr.wait();
} catch (InterruptedException e) {
}
}
}
mInputManager.start();
}
这里调用了mInputManager.start();然后
\android\frameworks\base\services\java\com\android\server\wm\InputManager.java
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart();
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
}
调用了nativeStart();然后
android\frameworks\base\services\jni\ com_android_server_InputManager.cpp
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Landroid/content/Context;"
"Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
。。。。。。。。。。。。。。。。。。。。。
然后本文件下
static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
if (checkInputManagerUnitialized(env)) {
return;
}
status_t result = gNativeInputManager->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
这里调用status_t result = gNativeInputManager->getInputManager()->start();然后
\android\frameworks\base\services\input\InputManager.cpp
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
LOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
LOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
终于找到了status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
然后仔细看这两个类并没有实现这个函数,因此他们会调用他们的父类public Thread里的 Run 方法我们来看这个
代码\android40\frameworks\base\libs\utils\ Threads.cpp
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
Mutex::Autolock _l(mLock);
if (mRunning) {
// thread already started
return INVALID_OPERATION;
}
// reset status and exitPending to their default value, so we can
// try again after an error happened (either below, or in readyToRun())
mStatus = NO_ERROR;
mExitPending = false;
mThread = thread_id_t(-1);
// hold a strong reference on ourself
mHoldSelf = this;
mRunning = true;
bool res;
if (mCanCallJava) {
res = createThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
} else {
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
mRunning = false;
mThread = thread_id_t(-1);
mHoldSelf.clear(); // "this" may have gone away after this.
return UNKNOWN_ERROR;
}
// Do not refer to mStatus here: The thread is already running (may, in fact
// already have exited with a valid mStatus result). The NO_ERROR indication
// here merely indicates successfully starting the thread and does not
// imply successful termination/execution.
return NO_ERROR;
// Exiting scope of mLock is a memory barrier and allows new thread to run
}
顺着函数一路跟踪下去会发现这个函数最终会无限执行Thread:: threadLoop()这个函数,这里就不多做说明了。
把代码位置贴出来 \android40\frameworks\base\libs\utils\ Threads.cpp的int Thread::_threadLoop(void*user)
函数。就在刚才的两个文件中我们下一步找到线程的: threadLoop()
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
到了这里这两个线程终于创建完成了,下面就可以开始事件的读取和处理了,我们从读取事件来讲,先读取事件才
能分发处理事件
二、 Input事件的读取
\android\frameworks\base\services\input\InputReader.cpp
void InputReader::loopOnce() {
。。。。。。。。。。。
。。。。。。
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
。。。。。。。。。
。。。。。。。。。。。
}
这里涉及到了刚才创建的EventHub,EventHub是与内核 打交道的模块,当碰到写的驱动不好用时,可能需要动这个
模块,因此重点来看这个模块代码,我们从getEvents这个函数看起在
\android40\frameworks\base\services\input\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize){
LOG_ASSERT(bufferSize >= 1); //buffsize必须>=1
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize]; //定义数据缓存 注解1
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;
LOGI("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;
LOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_REMOVED; //报告类型DEVICE_REMOVED
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break; //凑够bufferSize个就退出
}
}
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked(); //打开所有设备 注解2
mNeedToSendFinishedDeviceScan = true;
}
while (mOpeningDevices != NULL) { //报告被添加的最新设备
Device* device = mOpeningDevices;
LOGV("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; //发送一个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++];
//从mPendingEventItems中读取事件
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {//判定事件类型EPOLL_ID_INOTIFY /dev/input下
//文件有变化时才会发送这个事件 注解3
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) { //看事件类型是否为EPOLL_ID_WAKE 注解4
if (eventItem.events & EPOLLIN) {
LOGV("awoken after wake()");
awoken = true; //唤醒整个input系统
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);
//如果不是EPOLL_ID_INOTIFY和EPOLL_ID_WAKE 那么得到发送事件的设备的索引
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, //读取这个设备的event数据
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);
//计算这次读取的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; //将input_event转换成android的RawEvent类型
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; //凑够256就退出
break;
}
}
} else {
LOGW("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) {//如果有/dev/input下的设
//备有变化 并且还读取到了设备变化的事件
mPendingINotify = false;
readNotifyLocked(); //那么就打开这个设备 这个函数代码都在本目录下
//很简单能看懂 这里就不做说明了
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) { //如果有设备变化了 就跳过这次循环
continue;
}
//我们读取到了 事件,或者 需要唤醒input模块 那么就立即报告所有事件给android上层
// 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);
//读取mEpollFd的事件到mPendingEventItems 注解3 注解4
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) {
LOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
// Some events occurred.
mPendingEventCount = size_t(pollResult);
// On an SMP system, it is possible for the framework to read input events
// faster than the kernel input device driver can produce a complete packet.
// Because poll() wakes up as soon as the first input event becomes available,
// the framework will often end up reading one event at a time until the
// packet is complete. Instead of one call to read() returning 71 events,
// it could take 71 calls to read() each returning 1 event.
//
// Sleep for a short period of time after waking up from the poll() to give
// the kernel time to finish writing the entire packet of input events.
if (mNumCpus > 1) {
usleep(250);
}
}
}
// All done, return the number of events we read.
return event - buffer; //返回读取到事件个数
//到这里就把所读取到的事件送给InputReader::loopOnce()来处理了
}
注解1:
input_event 的定义,这个就是linux的input_event 的定义
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
注解2:
打开所有文件的方法
static const char *DEVICE_PATH = "/dev/input"; //定义input设备的目录
void EventHub::scanDevicesLocked() {
status_t res = scanDirLocked(DEVICE_PATH); //扫描这个目录
if(res < 0) {
LOGE("scan dir failed for %s\n", DEVICE_PATH);
}
}
status_t EventHub::scanDirLocked(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname); //打开这个目录 这个函数实现在\android40\bionic\libc\unistd\opendir.c
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) { //打开其中的一个文件
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);//这个函数很重要 看下面
}
closedir(dir);
return 0;
}
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
LOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR);打开这个目录
if(fd < 0) {
LOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
InputDeviceIdentifier identifier;
// Get device name.名称
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name.setTo(buffer);
}
// Check to see if the device is on our excluded list 检查这个设备是否在我们的链表上
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
LOGI("ignoring event id %s driver %s\n", devicePath, item.string());
close(fd);
return -1;
}
}
// Get device driver version. 驱动版本
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
LOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
// Get device identifier. 设备ID
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
LOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
// Get device physical location.//物理地址
if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.location.setTo(buffer);
}
// Get device unique id.
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);
}
// Make file descriptor non-blocking for use with poll().
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
LOGE("Error %d making device file descriptor non-blocking.", errno);
close(fd);
return -1;
}
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
#if 0
LOGI("add device %d: %s\n", deviceId, devicePath);
LOGI(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
" version %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
LOGI(" name: \"%s\"\n", identifier.name.string());
LOGI(" location: \"%s\"\n", identifier.location.string());
LOGI(" unique id: \"%s\"\n", identifier.uniqueId.string());
LOGI(" driver: v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
#endif
// Load the configuration file for the device.加载设备的配置文件
loadConfigurationLocked(device);
// Figure out the kinds of events the device reports.读取设备 数据的类型
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
//这个设备如果是键盘 那么是否含有 游戏手柄的特殊键 有的话做特殊处理
// 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;
}
// See if this is a cursor device such as a trackball or mouse. 莫非是鼠标?
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
// See if this is a touch pad.还是触摸板
// Is this a new modern multi-touch driver?竟然多点触控?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask) //靠x y坐标来判定
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// Some joysticks such as the PS3 controller report axes that conflict//所以不准还要加点别的
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { //也就是这个BTN_TOUCH,
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touch driver? 还是单点的简单
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
&& test_bit(ABS_X, device->absBitmask)
&& test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
}
/********************************************************************************
从上面的分析来看要确定这个设备
多点触控需要报三个bit位
ABS_MT_POSITION_X ABS_MT_POSITION_Y BTN_TOUCH
单点触控也是三个bit位
ABS_X ABS_Y BTN_TOUCH
鼠标三个bit位
REL_X REL_Y BTN_MOUSE
********************************************************************************/
// See if this device is a joystick.游戏杆
// Assumes that joysticks always have gamepad buttons in order to distinguish them
// from other devices such as accelerometers that also have absolute axes.
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
if (test_bit(i, device->absBitmask)
&& (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
}
}
}
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
if (test_bit(i, device->swBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_SWITCH;
break;
}
}
// Configure virtual keys. 虚拟按键处理这个也很重要现在一般触摸屏上带的按键都是这么处理的
//下面会单独讲 注解5
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
status_t status = loadVirtualKeyMapLocked(device);
LOGE("Get virtualkeys\n");
if (!status) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; //给设备增加一个 键盘类
}
}
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);//获取设备的keymap文件 这个对于按键设备来讲比较
} //重要也比较复杂 可以百度 android keymap
// Configure the keyboard, gamepad or virtual keyboard.配置键盘类的设备
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus
&& mBuiltInKeyboardId == -1
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
}
}
}
/********************************************************************************
注意上面device->classes的赋值方式 (除了joystick)其他都是 “|=” 也就是说一个设备其实可以
识别出多个类这也是为什么一个触摸屏可以发送按键指令的原因
********************************************************************************/
//有些设备不支持 比如有些传感器走的也是input设备 但是他们由单独的传感器模块处理而不是有eventhub,因此这里他们会被排除掉。
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
LOGV("Dropping device: id=%d, path='%s', name='%s'",
deviceId, devicePath, device->identifier.name.string());
delete device;
return -1;
}
//判断是内部设备还是外部设备看代码判定是外部设备有两个条件一是配置文件中有
//"device.internal"这个配置项就是内部设备其他为外部设备,一是总线类型是usb 或者 蓝牙就是外部设备。
// Determine whether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
//注册一个epoll来监听这个设备的事件。
//epoll是内核提供的一个监听设备的一个方法跟select类似不过比select更高效而且没有个数的限制
// 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;
}
LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
toString(mBuiltInKeyboardId == deviceId));
mDevices.add(deviceId, device);//将设备加入到已打开设备链表中
device->next = mOpeningDevices;
mOpeningDevices = device;
return 0;
}
下面对两个小方面做一下说明:
1.打开配置文件,上面函数调用的 loadConfigurationLocked(device);
void EventHub::loadConfigurationLocked(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.isEmpty()) {
LOGD("No input device configuration file found for device '%s'.",
device->identifier.name.string());
} else {
status_t status = PropertyMap::load(device->configurationFile,
&device->configuration);
if (status) {
LOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
device->identifier.name.string());
}
}
}
调用getInputDeviceConfigurationFilePathByDeviceIdentifier(device->identifier,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
这个函数的定义在android\frameworks\base\libs\ui\Input.cpp中
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
//首先从厂商编号、设备编号和设备驱动版本号去获取设备文件
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
}
}
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor,deviceIdentifier.product),//如果没有成功就从厂商编号
type)); //和设备编号去获取
if (!productPath.isEmpty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);//如果还没有的话就
//从设备名去获取
}
上面调用getInputDeviceConfigurationFilePathByName()这个函数也在这个文件里,因为很简单就不贴出来它
的作用是首先从ANDROID_ROOT/usr/idc目录下去找相应名字的文件并返回完整的路径名,如果找不到就从
ANDROID_DATA/system/devices/idc下面去找,这里ANDROID_ROOT一般指的是/system目录 ANDROID_DATA
一般指/data目录.总结来看安卓为输入设备打开配置文件依次会访问
/system/usr/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
/system/usr/idc/Vendor_XXXX_Product_XXXX.idc
/system/usr/idc/DEVICE_NAME.idc
/data/system/devices/idc/Vendor_XXXX_Product_XXXX_Version_XXXX.idc
/data/system/devices/idc/Vendor_XXXX_Product_XXXX.idc
/data/system/devices/idc/DEVICE_NAME.idc
我们以后在做输入设备配置文件的时候一定要注意顺序,别改了半天不好用。官网上也有这个的说明上地址
http://source.android.com/devices/tech/input/input-device-configuration-files.html
2.打开按键映射文件,上面调用loadKeyMapLocked(device);
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
这里调用keyMap.load()它的定义在
android\frameworks\base\libs\ui\Keyboard.cpp
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if(deviceConfiguration->tryGetProperty(String8("keyboard.layout"),//查找配置文件中keyboard.layout
keyLayoutName)) { //项 来加载layout文件
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
if (status == NAME_NOT_FOUND) {
LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), //查找配置文件中
keyCharacterMapName)) { //keyboard.characterMap项 来加载characterMap文件
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
LOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
if (isComplete()) {
return OK;
}
}
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK; //如果没有成功就靠 厂商id 设备id 驱动版本 等来查找
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { //如果还没有成功就打开"Generic"
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { //还没有就打开 "Virtual"
return OK;
}
// Give up!
LOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.string());
return NAME_NOT_FOUND;
}
总结
从上面可以看出安卓打开layout文件的依次顺序为
config 定义的
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl
官方介绍网址http://source.android.com/devices/tech/input/key-layout-files.html
characterMap的打开顺序为
config定义的
/system/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/system/usr/keychars/DEVICE_NAME.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX.kcm
/data/system/devices/keychars/DEVICE_NAME.kcm
/system/usr/keychars/Generic.kcm
/data/system/devices/keychars/Generic.kcm
/system/usr/keychars/Virtual.kcm
/data/system/devices/keychars/Virtual.kcm
官方介绍网址http://source.android.com/devices/tech/input/key-character-map-files.html
注解3
这个设计的epoll的操作
在EventHub::EventHub(void)中有如下代码
mEpollFd = epoll_create(EPOLL_SIZE_HINT); //创建一个 epoll文件
mINotifyFd = inotify_init(); // 用来监控/dev/input/目录下的文件改动
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;//添加的监控设备所要发送的事件类型
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
//将mINotifyFd添加到mEpollFd的监控中
在EventHub::getEvents中有如下代码
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//取出mEpollFd的事件放在数组mPendingEventItems中
注解4
在EventHub::EventHub(void)中有如下代码
int wakeFds[2];
result = pipe(wakeFds); //创建管道
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);//注册这个管道
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
//将read管道添加到mEpollFd的监控中
//这个的作用时是当向mWakeWritePipeFd写入数据时 ,系统就会通过mEpollFd发送事件,然后就可以通过
//mWakeReadPipeFd将发送的数据读出来.mWakeWritePipeFd的调用是在InputReader::requestRefreshConfiguration
//中这里下面会讲到,它的作用是 唤醒整个input系统 同样的会利用EventHub::getEvents中的。
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//将获取的事件添加到mPendingEventItems中
注解5
关于虚拟按键配置文件,处理代码在android\frameworks\base\services\input\EventHub.cpp中
status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
// The virtual key map is supplied by the kernel as a system board property file.
String8 path;
path.append("/sys/board_properties/virtualkeys.");
path.append(device->identifier.name);//虚拟按键配置文件在/sys/board_properties/virtualkeys.name中
if (access(path.string(), R_OK)) {
return NAME_NOT_FOUND;
}
return VirtualKeyMap::load(path, &device->virtualKeyMap);
}
这里调用irtualKeyMap::load(path, &device->virtualKeyMap);
在android\frameworks\base\libs\ui\VirtualKeyMap.cpp中
status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap){
*outMap = NULL;
Tokenizer* tokenizer;
status_t status = Tokenizer::open(filename, &tokenizer);//打开文件获得tokenizer
if (status) {
LOGE("Error %d opening virtual key map file %s.", status, filename.string());
} else {
VirtualKeyMap* map = new VirtualKeyMap();
if (!map) {
LOGE("Error allocating virtual key map.");
status = NO_MEMORY;
} else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
Parser parser(map, tokenizer);
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
LOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (status) {
delete map;
} else {
*outMap = map; //返回 map的指针
}
}
delete tokenizer;
}
return status;
}
至此打来了虚拟按键的配置文件
三、Input事件的处理
终于读取到了input事件,说道事件的处理我们还得继续来看void InputReader::loopOnce()中的事件处理
void InputReader::loopOnce() {
int32_t timeoutMillis;
{
。。。。。。。。
。。。。。。。。。。
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
if (count) {
processEventsLocked(mEventBuffer, count);
}
。。。。。。。。。。。。。。
}
这里调用 processEventsLocked(mEventBuffer, count);
在android\frameworks\base\services\input\InputReader.cpp文件中
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) { //循环处理所有事件
int32_t type = rawEvent->type;
size_t batchSize = 1;
//如果事件的类型比FIRST_SYNTHETIC_EVENT 说明这是个kernel传来的input事件
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
//找到属于同一个设备所有事件 或者 同一设备 但类型为FIRST_SYNTHETIC_EVENT之
//前的所有事件 ,从这里可以看出如果要系统处理一次的操作,那么驱动层必须发送
//FIRST_SYNTHETIC_EVENT事件,或者造成设备切换,设备切换不是我们能控制的因此我
//们必须发送FIRST_SYNTHETIC_EVENT事件来告诉系统这次操作完成了。
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
LOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//处理这些事件 注解7
} else { //这个是设备变化的事件
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED: //增加设备
addDeviceLocked(rawEvent->when, rawEvent->deviceId); //注解6
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;
}
}
在这里我们看到所有的事件都得到了处理,但是处理的方式还没看,要处理事件首先要有设备,
因此我们先看addDeviceLocked()这个函数
注解6
关于增加设备
addDeviceLocked(rawEvent->when, rawEvent->deviceId)代码在当前文件下
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
String8 name = mEventHub->getDeviceName(deviceId); //得到设备名字
uint32_t classes = mEventHub->getDeviceClasses(deviceId);//得到设备类型
InputDevice* device = createDeviceLocked(deviceId, name, classes);//创建一个inputreader层的设备
device->configure(when, &mConfig, 0); //配置一些东西 ----------------------------+-
device->reset(when); //复位 ||
||
if (device->isIgnored()) {//看mapper是否添加成功了 ||
LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string());||
} else { ||
LOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(), ||
device->getSources()); ||
} ||
||
ssize_t deviceIndex = mDevices.indexOfKey(deviceId); ||
if (deviceIndex < 0) { ||
mDevices.add(deviceId, device); //添加新建的设备 ||
} else { ||
LOGW("Ignoring spurious device added event for deviceId %d.", deviceId); ||
delete device; ||
return; ||
} ||
||
这个函数里调用了createDeviceLocked() 和 configure()我们一一来看 ||
createDeviceLocked()在当前文件中代码 <------------------------------------ |
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, |
const String8& name, uint32_t classes) { |
InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);//创建一个新设备 |
|
// External devices. |
if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { //为外部设备 设置专门属性 |
device->setExternal(true); |
} |
//为switch设备添加mapper文件inputmapper文件是事件的映射文件,负责把事件处理后输送给相应的上层app |
//这个设备很少用到我们就暂时不分析了代码在InputReader.cpp中 需要的自己看一下 |
// Switch-like devices. |
if (classes & INPUT_DEVICE_CLASS_SWITCH) { |
device->addMapper(new SwitchInputMapper(device)); |
} |
|
// Keyboard-like devices. |
uint32_t keyboardSource = 0; |
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; |
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { |
//设置按键类设备的mapper属性这里添加的mapper文件只有基本属性,并没有具体的内容具体配置是 |
//在config中进行的 |
keyboardSource |= AINPUT_SOURCE_KEYBOARD; |
} |
if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { |
keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; |
} |
if (classes & INPUT_DEVICE_CLASS_DPAD) { |
keyboardSource |= AINPUT_SOURCE_DPAD; |
} |
if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { |
keyboardSource |= AINPUT_SOURCE_GAMEPAD; |
} |
|
if (keyboardSource != 0) { //为按键类设备添加mapper文件 |
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); |
} |
|
// Cursor-like devices. |
if (classes & INPUT_DEVICE_CLASS_CURSOR) { //为鼠标类设备添加mapper 文件 |
device->addMapper(new CursorInputMapper(device)); |
} |
|
// Touchscreens and touchpad devices. |
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { //触控类添加mapper文件 |
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) { //游戏杆类添加mapper文件 |
device->addMapper(new JoystickInputMapper(device)); |
} |
|
return device; |
} |
|
对于上面的addMapper这个函数 |
void InputDevice::addMapper(InputMapper* mapper) { |
mMappers.add(mapper); |
} |
|
这里调用了mMappers.add(mapper);我没找到这add方法的实现代码在哪?哪位大神可以提示一下最好。在此先谢过 |
不过createDeviceLocked这个函数还是很简单的,就是一些新建设备和mapper文件,具体的操作都没有涉及到 |
|
下面看InputDevice::configure 也在这个文件中 <----------------------------
void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes){
mSources = 0;
if (!isIgnored()) { //mapper文件不为空
if (!changes) { // first time only
mContext->getEventHub()->getConfiguration(mId, &mConfiguration); //找到相应的idc文件
}
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->configure(when, config, changes);
mSources |= mapper->getSources();
}
}
}
上面调用的getConfiguration()在 EventHub.cpp中
先看getConfiguration()这个函数的作用是,返回设备的配置文件(即idc文件)指针。
void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device && device->configuration) {
*outConfiguration = *device->configuration;
} else {
outConfiguration->clear();
}
}
调用mapper->configure(when, config,changes)这里跟每个设备有关,这里只能分开来讲,我们从多点触控看起吧
多点触控的mapper定义为为MultiTouchInputMapper,但是这个类里面没有configure的定义那么我只能来看他的父类
TouchInputMapper中configure的定义代码在InputReader.cpp中
void TouchInputMapper::configure(nsecs_t when,
const InputReaderConfiguration* config, uint32_t changes) {
InputMapper::configure(when, config, changes);
mConfig = *config;
if (!changes) { // first time only
// Configure basic parameters.
configureParameters(); //见下面 |
V
/************************************************************************************************
上面调用的configureParameters()函数
void TouchInputMapper::configureParameters() {
// Use the pointer presentation mode for devices that do not supportdistinct
//基于指针的配置并不明显的支持多点触控,这里的配置能够准确的配置两点或以上设备
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(),INPUT_PROP_SEMI_MT)
//从驱动里确定device 是多点触控 还是指针,还是点设备
? Parameters::GESTURE_MODE_POINTER : Parameters::GESTURE_MODE_SPOTS;
String8 gestureModeString;
//配置文件中找touch.gestureMode这个条目 来确定设备的类型
if(getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
gestureModeString)) {
if (gestureModeString == "pointer") { //如果指针
mParameters.gestureMode = Parameters::GESTURE_MODE_POINTER;
} else if (gestureModeString == "spots") { //点设备
mParameters.gestureMode = Parameters::GESTURE_MODE_SPOTS;
} else if (gestureModeString != "default") { //默认
LOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string());
}
}
//从驱动文件里确定 触控设备的类型
if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
// The device is a touch screen.
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; //触摸屏
} else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
// The device is a pointing device like a track pad.
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; //指针设备(多指鼠标)
} else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) //传送的坐标是相对坐标
|| getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { //y也是相对坐标
// The device is a cursor device with a touch pad attached.
// By default don't use the touch pad to move the pointer.
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; //那就是触摸板
} else {
// The device is a touch pad of unknown purpose.
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; //指针设备
}
String8 deviceTypeString;
if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
deviceTypeString)) {
//从配置文件中确定设备类型
if (deviceTypeString == "touchScreen") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; //触摸屏
} else if (deviceTypeString == "touchPad") {
mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; //触摸板
} else if (deviceTypeString == "pointer") {
mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; //指针设备
} else if (deviceTypeString != "default") {
LOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string());
}
}
mParameters.orientationAware = mParameters.deviceType ==Parameters::DEVICE_TYPE_TOUCH_SCREEN;
getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
mParameters.orientationAware);
//idc文件中找"touch.orientationAware"这个条目来确定屏幕的方向
mParameters.associatedDisplayId = -1;
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware
|| mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
|| mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
mParameters.associatedDisplayIsExternal =
mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
&& getDevice()->isExternal();
mParameters.associatedDisplayId = 0;
}
}
*******************************************************************************************/
// Configure common accumulators.配置光标和按键加速,都是根据驱动文件或者idc文件
mCursorScrollAccumulator.configure(getDevice());
mTouchButtonAccumulator.configure(getDevice());
// Configure absolute axis information.将所有坐标相关初始值清除
代码在本文件中的void RawPointerAxes::clear()
configureRawPointerAxes();
//然后调用voidMultiTouchInputMapper::configureRawPointerAxes()也在本文件中,它是从
//驱动文件里面找到一些配置然后读取出来 这些代码就不贴出来了太多了 而且意义不大
// Prepare input device calibration. 准备 输入设备标准把硬件信息 加入到 建立的虚拟设备中例如
parseCalibration();
//分辨率 屏幕方向 显示区域等信息 代码很长不贴了
resolveCalibration();
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) {
// Update pointer speed. //校准 指针速度
mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters);
mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters);
//简单的校准参数赋值 代码很简单都在本文件中 这里就不贴了
}
bool resetNeeded = false;
if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
| InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
| InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {
// Configure device sources, surface dimensions, orientation and
// scaling factors.
configureSurface(when,&resetNeeded);----------------------------------------------------------
} |
|
if (changes && resetNeeded) { |
//Send reset, unless this is the first time the device has beenconfigured,除非这个设备是第一次配置, |
//in which case the reader will call reset itself after all mappers areready否则在这里它将被reset一次 |
getDevice()->notifyReset(when); |
} |
} |
|
/********************************************************************************************** <-----
下面看 configureSurface(when, &resetNeeded); 在InputReader.cpp中
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
int32_t oldDeviceMode = mDeviceMode;
// Determine device mode. 通过设备的类型来确定mSource和mDeviceMode。
if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER
&& mConfig.pointerGesturesEnabled) {
mSource = AINPUT_SOURCE_MOUSE;
mDeviceMode = DEVICE_MODE_POINTER;
} else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
&& mParameters.associatedDisplayId >= 0) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
mDeviceMode = DEVICE_MODE_DIRECT;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DEVICE_MODE_UNSCALED;
}
// Ensure we have valid X and Y axes.从驱动中读出来的配置中 x和y的值 是有效的
if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
LOGW(INDENT "Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.", getDeviceName().string());
mDeviceMode = DEVICE_MODE_DISABLED;
return;
}
// Get associated display dimensions.
if (mParameters.associatedDisplayId >= 0) {
if (!mConfig.getDisplayInfo(mParameters.associatedDisplayId //得到显示屏的分辨率 代码在本文件中
mParameters.associatedDisplayIsExternal, |
&mAssociatedDisplayWidth, &mAssociatedDisplayHeight, |
&mAssociatedDisplayOrientation)) { |
LOGI(INDENT "Touch device '%s' could not query the properties of its associated " |
"display %d. The device will be inoperable until the display size " |
"becomes available.", |
getDeviceName().string(), mParameters.associatedDisplayId); |
mDeviceMode = DEVICE_MODE_DISABLED; |
return; |
} |
} V
/***********************************************************************************************
bool InputReaderConfiguration::getDisplayInfo(int32_t displayId, bool external,
int32_t* width, int32_t* height, int32_t* orientation) const {
if (displayId == 0) {
const DisplayInfo& info = external ? mExternalDisplay : mInternalDisplay;
//这里的关键是 mExternalDisplay和mInternalDisplay在哪里初始化的值
if (info.width > 0 && info.height > 0) { //其实这是在另外一个进程里设置的 过程比较复杂
if (width) {
//参考http://wenku.baidu.com/view/1c25e14a2e3f5727a5e9621f.html
*width = info.width;
}
if (height) {
*height = info.height;
}
if (orientation) {
*orientation = info.orientation;
}
return true;
}
}
return false;
}
************************************************************************************************/
// Configure dimensions. 根据上面确定的mDeviceMode 来确定 实际可用的分辨率,
int32_t width, height, orientation;
if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
width = mAssociatedDisplayWidth; //真实的分辨率
height = mAssociatedDisplayHeight;
orientation = mParameters.orientationAware ?
mAssociatedDisplayOrientation : DISPLAY_ORIENTATION_0;
} else {
//驱动中读出来的分辨率经过计算得到的
width = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
height = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
orientation = DISPLAY_ORIENTATION_0;
}
// If moving between pointer modes, need to reset some state.
//设备类型发生了改变这说明前面的配置可能发生某种意外 清除一些状态 下面重新来配置
bool deviceModeChanged;
if (mDeviceMode != oldDeviceMode) {
deviceModeChanged = true;
mOrientedRanges.clear();
}
// Create pointer controller if needed.
if (mDeviceMode == DEVICE_MODE_POINTER ||
(mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
if (mPointerController == NULL) {
mPointerController = getPolicy()->obtainPointerController(getDeviceId());
//建立一个pointer controller
//pointercontroller 是神马东西呢我们可以找到他们的代码
//android\frameworks\base\services\input\PointerController.cpp中,这里就不分析代码了,实在太多了,仅
//对它的作用说明一下:当我们触摸屏幕或者移动鼠标时,我们有时候需要在屏幕上
触摸点或者鼠标光标的位置,
//pointer controller 就是同步坐标和显示位置用的
}
} else {
mPointerController.clear();
}
char hwrotBuf[PROPERTY_VALUE_MAX];
int32_t hwrotation;
property_get("ro.sf.hwrotation", hwrotBuf, "0");
hwrotation = atoi(hwrotBuf) / 90;
orientation = (orientation + hwrotation ) % 4;
if (hwrotation == DISPLAY_ORIENTATION_90 ||
hwrotation == DISPLAY_ORIENTATION_270) {
int tmp = width;
width = height;
height = tmp;
}
bool orientationChanged = mSurfaceOrientation != orientation;
if (orientationChanged) {
mSurfaceOrientation = orientation;
}
bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;
if (sizeChanged || deviceModeChanged) {
LOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d, mode is %d",
getDeviceId(), getDeviceName().string(), width, height, mDeviceMode);
mSurfaceWidth = width;
mSurfaceHeight = height;
// Configure X and Y factors.
mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1);
mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1);
mXPrecision = 1.0f / mXScale;
mYPrecision = 1.0f / mYScale;
mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X;//如果分辨率有了改变就需要重新配置一些参数
mOrientedRanges.x.source = mSource;
mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y;
mOrientedRanges.y.source = mSource;
configureVirtualKeys(); //配置虚拟按键 代码在本文件中 |
V
/*****************************************************************************************
void TouchInputMapper::configureVirtualKeys() {
Vector<VirtualKeyDefinition> virtualKeyDefinitions;
getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
//这里有个新数据类型Vector,他的作用我们做个说明
//vector是C++标准模板库中的部分内容,中文偶尔译作“容器”,但并不准确。它是一个多功能的,
//能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够
//像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增
//加和压缩数据。
mVirtualKeys.clear();
if (virtualKeyDefinitions.size() == 0) {
return;
}
mVirtualKeys.setCapacity(virtualKeyDefinitions.size());
int32_t touchScreenLeft = mRawPointerAxes.x.minValue; //确定按键的 坐标和大小
int32_t touchScreenTop = mRawPointerAxes.y.minValue;
int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
const VirtualKeyDefinition& virtualKeyDefinition =
virtualKeyDefinitions[i];
//得到按键的定义(虚拟按键配置文件中读出来的)
mVirtualKeys.add(); //添加这个按键
VirtualKey& virtualKey = mVirtualKeys.editTop();
virtualKey.scanCode = virtualKeyDefinition.scanCode;
int32_t keyCode;
uint32_t flags;
if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode,
& keyCode, & flags)) {
LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
virtualKey.scanCode);
mVirtualKeys.pop(); // drop the key 失败了。。。
continue;
}
virtualKey.keyCode = keyCode;
virtualKey.flags = flags;
// convert the key definition's display coordinates into touch coordinates for a hitbox
int32_t halfWidth = virtualKeyDefinition.width / 2;
int32_t halfHeight = virtualKeyDefinition.height / 2;
//按键的上下左右坐标
virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
* touchScreenWidth / mSurfaceWidth + touchScreenLeft;
virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
* touchScreenWidth / mSurfaceWidth + touchScreenLeft;
virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
* touchScreenHeight / mSurfaceHeight + touchScreenTop;
virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
* touchScreenHeight / mSurfaceHeight + touchScreenTop;
}
}
********************************************************************************************/
剩下的就 不多说了 全是参数的配置
。。。。。。。。。
。。。。。。。。。
。。。。。。。。。
}
*************************************************************************************************/
注解7:
关于InputReader中国事件的处理,input事件的处理代码在当前文件下
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId); //得到发生input事件的设备索引
if (deviceIndex < 0) {
LOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex); //得到device
if (device->isIgnored()) {
//LOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
device->process(rawEvents, count); //事件处理 |
} V
/************************************************************************************************
这里调用>process(rawEvents, count);来处理代码在
android\frameworks\base\services\input\InputReader.cpp中
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper. 让每个设备的 mapper处理相应的input事件
// 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++) {
。。。。。
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {
mDropUntilNextSync = false;
.........
} else {
.............
}
} else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_DROPPED) {
LOGI("Detected input event buffer overrun for device %s.", mName.string());
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent); //通过每个设备mapper 来处理事件我们来看多点触控的
} |
} |
} |
} |
V
上面调用的 在本文件中
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
TouchInputMapper::process(rawEvent);
mMultiTouchMotionAccumulator.process(rawEvent);
}
继续看mMultiTouchMotionAccumulator.process(rawEvent); 也在本文件中
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
if (rawEvent->type == EV_ABS) { //只处理 EV_ABS EV_SYN 和 SYN_MT_REPORT
bool newSlot = false;
if (mUsingSlotsProtocol) {
if (rawEvent->scanCode == ABS_MT_SLOT) { //多点触控槽这个我也不知道怎么翻译好,但是具体
mCurrentSlot = rawEvent->value; //作用我知道的很清楚指的是多点触控所支持的最大点数
newSlot = true;
}
} else if (mCurrentSlot < 0) {
mCurrentSlot = 0;
}
//触摸屏所支持的最大点数设置错误 就报个错
if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
#if DEBUG_POINTERS
if (newSlot) {
LOGW("MultiTouch device emitted invalid slot index %d but it "
"should be between 0 and %d; ignoring this slot.",
mCurrentSlot, mSlotCount - 1);
}
#endif
} else {
Slot* slot = &mSlots[mCurrentSlot];
switch (rawEvent->scanCode) { //解析所有的合法的多点触控数据
case ABS_MT_POSITION_X: //X坐标
slot->mInUse = true;
slot->mAbsMTPositionX = rawEvent->value;
break;
case ABS_MT_POSITION_Y://Y坐标
slot->mInUse = true;
slot->mAbsMTPositionY = rawEvent->value;
break;
case ABS_MT_TOUCH_MAJOR: //椭圆形接触点的长轴 这个值会随着压力的增大而增大
slot->mInUse = true;
slot->mAbsMTTouchMajor = rawEvent->value;
break;
case ABS_MT_TOUCH_MINOR: //椭圆形接触点的短轴
slot->mInUse = true;
slot->mAbsMTTouchMinor = rawEvent->value;
slot->mHaveAbsMTTouchMinor = true;
break;
case ABS_MT_WIDTH_MAJOR: //把形成触点的手指 看成一个椭圆 它的 长轴 个通常是个常量
slot->mInUse = true;
slot->mAbsMTWidthMajor = rawEvent->value;
break;
case ABS_MT_WIDTH_MINOR: //把形成触点的手指 看成一个椭圆 它的 短轴
slot->mInUse = true;
slot->mAbsMTWidthMinor = rawEvent->value;
slot->mHaveAbsMTWidthMinor = true;
break;
case ABS_MT_ORIENTATION: //椭圆的方向
slot->mInUse = true;
slot->mAbsMTOrientation = rawEvent->value;
break;
case ABS_MT_TRACKING_ID: //触点的ID
if (mUsingSlotsProtocol && rawEvent->value < 0) {
// The slot is no longer in use but it retains its previous contents,
// which may be reused for subsequent touches.
slot->mInUse = false;
} else {
slot->mInUse = true;
slot->mAbsMTTrackingId = rawEvent->value;
}
break;
case ABS_MT_PRESSURE: //压力
slot->mInUse = true;
slot->mAbsMTPressure = rawEvent->value;
break;
case ABS_MT_DISTANCE: //触点 悬停 距离
slot->mInUse = true;
slot->mAbsMTDistance = rawEvent->value;
break;
case ABS_MT_TOOL_TYPE: //类型
slot->mInUse = true;
slot->mAbsMTToolType = rawEvent->value;
slot->mHaveAbsMTToolType = true;
break;
}
}
} else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
mCurrentSlot += 1;
}
}
voidMultiTouchInputMapper::process()执行完了,比较坑爹的是MultiTouchInputMapper的父类还有一个
process接下来系统会运行这个函数
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);
}
}
这个函数会处理sync事件
void TouchInputMapper::sync(nsecs_t when) {
// Sync button state.
mCurrentButtonState = mTouchButtonAccumulator.getButtonState() //同步按键状态
| mCursorButtonAccumulator.getButtonState();
// Sync scroll state.
mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); //滚动条状态
mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
mCursorScrollAccumulator.finishSync(); //清除以前信息
// Sync touch state.
bool havePointerIds = true;
mCurrentRawPointerData.clear();
syncTouch(when, &havePointerIds);--------
|
。。。。。。 |
。。。。。。 V
/*************************************************************************************
void MultiTouchInputMapper::syncTouch(nsecs_t when, bool* outHavePointerIds) {
size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
size_t outCount = 0;
BitSet32 newPointerIdBits;
for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
const MultiTouchMotionAccumulator::Slot* inSlot =
mMultiTouchMotionAccumulator.getSlot(inIndex);
if (!inSlot->isInUse()) {
continue;
}
if (outCount >= MAX_POINTERS) {
........
break; // too many fingers!
}
//把事件信息放到 mCurrentRawPointerData.pointers中 供后面使用
RawPointerData::Pointer& outPointer = mCurrentRawPointerData.pointers[outCount];
outPointer.x = inSlot->getX();
outPointer.y = inSlot->getY();
outPointer.pressure = inSlot->getPressure();
outPointer.touchMajor = inSlot->getTouchMajor();
outPointer.touchMinor = inSlot->getTouchMinor();
outPointer.toolMajor = inSlot->getToolMajor();
outPointer.toolMinor = inSlot->getToolMinor();
outPointer.orientation = inSlot->getOrientation();
outPointer.distance = inSlot->getDistance();
outPointer.tiltX = 0;
outPointer.tiltY = 0;
outPointer.toolType = inSlot->getToolType();
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = mTouchButtonAccumulator.getToolType();
if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
}
}
bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE
&& (mTouchButtonAccumulator.isHovering()
|| (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));
outPointer.isHovering = isHovering;
// Assign pointer id using tracking id if available. //看看 点的ID是否有效
if (*outHavePointerIds) {
int32_t trackingId = inSlot->getTrackingId();
int32_t id = -1;
if (trackingId >= 0) {
for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
uint32_t n = idBits.clearFirstMarkedBit();
if (mPointerTrackingIdMap[n] == trackingId) {
id = n;
}
}
if (id < 0 && !mPointerIdBits.isFull()) {
id = mPointerIdBits.markFirstUnmarkedBit();
mPointerTrackingIdMap[id] = trackingId;
}
}
if (id < 0) {
*outHavePointerIds = false;
mCurrentRawPointerData.clearIdBits();
newPointerIdBits.clear();
} else {
outPointer.id = id;
mCurrentRawPointerData.idToIndex[id] = outCount;
mCurrentRawPointerData.markIdBit(id, isHovering);
newPointerIdBits.markBit(id);
}
}
outCount += 1;
}
mCurrentRawPointerData.pointerCount = outCount;
mPointerIdBits = newPointerIdBits;
mMultiTouchMotionAccumulator.finishSync();
}
************************************************************************************/
// Reset state that we will compute below.
mCurrentFingerIdBits.clear(); //先清除一些数据
mCurrentStylusIdBits.clear();
mCurrentMouseIdBits.clear();
mCurrentCookedPointerData.clear();
if (mDeviceMode == DEVICE_MODE_DISABLED) {
// Drop all input if the device is disabled. //设备无效就把数据清除
mCurrentRawPointerData.clear();
mCurrentButtonState = 0;
} else {
// Preprocess pointer data. //否则就处理他们
if (!havePointerIds) {
assignPointerIds();
}
// Handle policy on initial down or hover events.
uint32_t policyFlags = 0;
bool initialDown = mLastRawPointerData.pointerCount == 0
&& mCurrentRawPointerData.pointerCount != 0;
bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;
if (initialDown || buttonsPressed) { //如果第一次摁下
// If this is a touch screen, hide the pointer on an initialdown.如果是个触摸屏,隐藏这个点
if (mDeviceMode == DEVICE_MODE_DIRECT) {
getContext()->fadePointer();
}
// Initial downs on external touch devices should wake thedevice.如果是外部设备就唤醒整个系统
// We don't do this for internal touch screens to prevent them from waking
// up in your pocket.//内部设备不用唤醒,因为有时候你仅仅是把设备放进口袋里
// TODO: Use the input device configuration to control this behavior more finely.
if (getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
}
。。。。。。。。。
// Cook pointer data. This call populates the mCurrentCookedPointerData structure
// with cooked pointer data that has the same ids and indices as the raw data.
// The following code can use either the raw or cooked data, as needed.
cookPointerData();//根据当前屏幕显示状态进行坐标转换关于这个官网有个很详细的介绍这里就不做说
//明了给出网址http://source.android.com/devices/tech/input/touch-devices.html
// Dispatch the touches either directly or by translation through a pointer on screen.
if (mDeviceMode == DEVICE_MODE_POINTER) { //如果是鼠标
。。。。。。。。。。。。。。
// Stylus takes precedence over all tools, then mouse, then finger.优先级 笔《鼠标《手指
PointerUsage pointerUsage = mPointerUsage;
if (!mCurrentStylusIdBits.isEmpty()) {
mCurrentMouseIdBits.clear();
mCurrentFingerIdBits.clear();
pointerUsage = POINTER_USAGE_STYLUS;
} else if (!mCurrentMouseIdBits.isEmpty()) {
mCurrentFingerIdBits.clear();
pointerUsage = POINTER_USAGE_MOUSE;
} else if (!mCurrentFingerIdBits.isEmpty() || isPointerDown(mCurrentButtonState)) {
pointerUsage = POINTER_USAGE_GESTURES;
}
dispatchPointerUsage(when, policyFlags, pointerUsage);//分发这个事件 这里不做说明我们从下
//面touch设备看起最终他俩是一个作用就是把事件放入到 待处理的队列中
} else {
if (mDeviceMode == DEVICE_MODE_DIRECT
&& mConfig.showTouches && mPointerController != NULL) {
mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
mPointerController->setButtonState(mCurrentButtonState);
mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
mCurrentCookedPointerData.touchingIdBits);
}
dispatchHoverExit(when, policyFlags);
dispatchTouches(when, policyFlags); ---------------------
dispatchHoverEnterAndMove(when, policyFlags); |
} V
/**********************************************************************************************
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
。。。。。。。。。。。。。。。。。。。。。。
if (currentIdBits == lastIdBits) {
if (!currentIdBits.isEmpty()) {
// No pointer id changes so this is a move event.点ID没有变化 那么这是一个MOVE 事件
// The listener takes care of batching moves so we don't have to deal with that here.
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,
AMOTION_EVENT_EDGE_FLAG_NONE,
mCurrentCookedPointerData.pointerProperties,
mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
currentIdBits, -1,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
} else { //有可能是 up down move 等事件
。。。。。。。。。。。。。。。
// Dispatch pointer up events. //up事件处理
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.clearFirstMarkedBit();
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0,
mLastCookedPointerData.pointerProperties,
mLastCookedPointerData.pointerCoords,
mLastCookedPointerData.idToIndex,
dispatchedIdBits, upId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
dispatchedIdBits.clearBit(upId);
}
// Dispatch move events if any of the remaining pointers moved from their old locations.
// Although applications receive new locations as part of individual pointer up
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded) { //move事件处理
LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
dispatchMotion(when, policyFlags, mSource,
AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,
mCurrentCookedPointerData.pointerProperties,
mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
dispatchedIdBits, -1,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
// Dispatch pointer down events using the new pointer locations.
while (!downIdBits.isEmpty()) {
uint32_t downId = downIdBits.clearFirstMarkedBit();
dispatchedIdBits.markBit(downId);
if (dispatchedIdBits.count() == 1) {
// First pointer is going down. Set down time.
mDownTime = when;
}
dispatchMotion(when, policyFlags, mSource, //down事件处理
AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,
mCurrentCookedPointerData.pointerProperties,
mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
dispatchedIdBits, downId,
mOrientedXPrecision, mOrientedYPrecision, mDownTime);
}
}
}
上面函数分发事件统一调用了dispatchMotion()这个函数我们来看这个
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
const uint32_t* idToIndex, BitSet32 idBits,
int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {
。。。。。。。。。。。
。。。。。。。。。。。。。
NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
action, flags, metaState, buttonState, edgeFlags,
pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime);
getListener()->notifyMotion(&args);
这里调用TouchInputMapper::getListener()->notifyMotion(&args)TouchInputMapper::getListener()调用
mContext->getListener(),
此mContext为InputReader::mContext,所以其getListener()返回的则为
InputReader::mQueuedListener,则最后调用QueuedInputListener::notifyMotion
最后我们找到实现代码在android\frameworks\base\services\input\InputListener.cpp中
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
mArgsQueue.push(new NotifyMotionArgs(*args));
}
这个函数是把 整理好的事件 放入到队列里。
************************************************************************************************/
。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。。。。。。
}
************************************************************************************************/
我们回头看
void InputReader::loopOnce() {
int32_t timeoutMillis;
{ // acquire lock
。。。。。。。。。。。。。。
processEventsLocked(mEventBuffer, count);
。。。。。。。。。。。。。。。。。。。。
// Flush queued events out to the listener.将队列里的事件发送到给各个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();
}
到了这里InputReader中的事件处理已经完成了,总的来讲,他们是把从kernel总读取的事件经过一系列的变换和
分类,分别存到相应的mQueuedListener::mArgsQueue队列中,然后将他们再分发出去。总结的很简单,但中间涉
及的
东西很多,上面也仅仅是简介而已。
上面我只分析了 多点触控的事件处理,其他设备的起始也类似,就不多做说明了。
接下来终于可以该看 InputDispatcher了。
我们接上面的mQueuedListener->flush();代码在android\frameworks\base\services\input\InputListener.cpp中
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();
}
这个函数是执行每个队列里的notify函数,我们先看这个参数mInnerListener,它是关系到Dispatcher和Reader
联系的重要桥梁。
我们来分析为什么。他是在QueuedInputListener类实例化的时候传进来的,代码
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
mInnerListener(innerListener) {
}
那么QueuedInputListener实例化在哪呢,继续找我们发现 他也是一个参数,在InputReader构造函数里里
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
不行的时发现这里listener还是一个参数,我们继续找InputReader的实例化代码,在InputManager的代码里
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
从这里看出mInnerListener这个参数就是mDispatcher,到了这里终于找到了Dispatcher和Reader的联系了。
我们继续看, args->notify()这个函数,文件里有很多类的args,我们以NotifyKeyArgs为例子进行说明。
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}
我们看到这里调用了Dispatcher的函数notifyKey(this)我们找到这个函数的实现代码
在android\frameworks\base\services\input\InputDispatcher.cpp中
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
。。。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。.
if (!motionEntry->canAppendSamples(args->action, //将事件复制到mInboundQueue中
args->pointerCount, args->pointerProperties)) {
// Last motion event in the queue for this device and source is
// not compatible for appending new samples. Stop here.
goto NoBatchingOrStreaming;
}
。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。
if (needWake) {
mLooper->wake();
}
}
这里调用mLooper->wake(); 终于可以执行InputDispatcher的线程了,在这之前的所有动作都是在
InputReader中执行的。
下面可以看 mDispatcher->dispatchOnce();这个函数了
在android\frameworks\base\services\input\InputDispatcher.cpp中
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
dispatchOnceInnerLocked(&nextWakeupTime); //事件处理----------------------------------
|
if (runCommandsLockedInterruptible()) { |
nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately |
} |
} // release lock |
|
// Wait for callback or timeout or wake. (make sure we round up, not down) |
nsecs_t currentTime = now(); |
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); |
mLooper->pollOnce(timeoutMillis); | |
} V |
|
这里调用的mLooper->pollOnce(timeoutMillis); 在android\frameworks\base\libs\utils\Looper.cpp中 |
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { |
int result = 0; |
for (;;) { |
while (mResponseIndex < mResponses.size()) { |
const Response& response = mResponses.itemAt(mResponseIndex++); |
ALooper_callbackFunc callback = response.request.callback; |
if (!callback) { |
int ident = response.request.ident; |
int fd = response.request.fd; |
int events = response.events; |
void* data = response.request.data; |
。。。。。 |
if (outFd != NULL) *outFd = fd; |
if (outEvents != NULL) *outEvents = events; |
if (outData != NULL) *outData = data; |
return ident; |
} |
} |
|
if (result != 0) { |
。。。。 |
if (outFd != NULL) *outFd = 0; |
if (outEvents != NULL) *outEvents = NULL; |
if (outData != NULL) *outData = NULL; |
return result; |
} |
|
result = pollInner(timeoutMillis);注意这里 这个函数会导致线程阻塞 一是超时 一是 epoll |
} | |
} | |
V |
|
int Looper::pollInner(int timeoutMillis) { |
|
。。。。。。。。。。。。。。。。。。。。。 |
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);//超时阻塞 |
。。。。。。。。。。。。。。。。 |
。。。。。。。。。。。 |
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//epoll阻塞 |
。。。。。。。。。. |
。。。。。。。。 |
} |
|
|
|
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { <------------------------
nsecs_t currentTime = now();
。。。。。。。。。。。。。。
mInboundQueue.dequeue(entry);//从事件队列里面取出事件
mPendingEvent = entry;
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}
}
// Now we have an event to dispatch.我们取得了 分发的事件
// All events are eventually dequeued and processed this way, even if we intend to drop them.
//所有从队列 里取出的事件都必须用这种方法处理,甚至这是一个要被丢弃的事件
。。。。。。。。。。。。。。。。。。。。。。。
switch (mPendingEvent->type) { //根据事件的类型分别分发下去
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
。。。。。。。。。
case EventEntry::TYPE_DEVICE_RESET: {
。。。。。。。。。。
case EventEntry::TYPE_KEY: {
。。。。。。。。
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime); //分发motion事件
break; |
} |
|
default: |
LOG_ASSERT(false); |
break; |
} |
。。。。。。。。。。。。。。。。。。。 |
} |
V
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
。。。。。。。。。。。。。。。。。。。。。。。。
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);分发事件
return true; |
} |
V
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
。。。。。。。。。。。。。。。。
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
resumeWithAppendedMotionSample);
。。。。。。。。。。。。。。。。 |
} |
V
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
。。。。。。。。。。。。。。。。。。。。。。。。。 //创建DispatchEntry对象并把它增加到
//Connection::outboundQueue队列中。
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget,
resumeWithAppendedMotionSample);
} |
V
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
。。。。。。。。。。。。。。。。。。。。。。。。
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
activateConnectionLocked(connection.get());
//把当前Connection增加到InputDispatcher::mActiveConnections链表中
startDispatchCycleLocked(currentTime,connection);
//Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中,
//调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它
//有了一个新的消息。
}
}
当我们发送了signal以后,相应的app就会收到这个signal,然后就会来取出相应的事件来处理。到这里事件的分发
就算已经完成了。
总结一下,总的来讲android层面input处理流程很顺,没有过多的分支,看起来其实相对简单,但是对于我这中新
手还是很有难度,有了分析这个的基础,以后再分析更难得
系统也就不会觉得特别的累了。 下面我们还有Kernel层
的input分析,放到另一个文档里面,