安卓input子系統是經過事件管道 到達系統的各個層。html
在最低層,物理輸入設備產生了描述的狀態變化的 信號,如按鍵 和 觸摸接觸點。而後進行編碼,並以某種方式發送這些信號,例如經過USB HID報告或I2C總線。這些信號被linux Kernel的驅動 解碼。驅動會把這些信號翻譯成 標準的事件類型 和 代碼。Linux內核 在linux/input.h 中定義了一套標準的事件類型和代碼。java
android的 EventHub 組件 經過打開dev/input 中的設備文件的方式從linux kernel讀取這些輸入事件,而後android的 InputReader 組件 會把這些linux輸入事件翻譯成一個android輸入事件流.。
linux
在翻譯過程當中涉及到device configuration, keyboard layout files, and various mapping tables.三個配置文件。最後InputReader 會把這些翻譯好的事件發送給合適的應用程序窗口。
總的來看android4.0的input子系統能夠分爲兩部分,安卓部分和linuxkernel部分。android
這個文檔咱們只分析Android部分,Kernel會在另外一篇文章裏說明。c++
Android開機時候會建立一個進程來啓動各類服務,其中有一個會啓動input的服務,這是android input的運行時候的核心,下面咱們具體來看。
1、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的系統的初始化和正常工做時候一些管理工做。app
看完代碼發現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;
}
到了這裏這兩個線程終於建立完成了,下面就能夠開始事件的讀取和處理了,咱們從讀取事件來說,先讀取事件才
能分發處理事件
2、 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; } 至此打來了虛擬按鍵的配置文件 3、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分析,放到另外一個文檔裏面,