下面這是基於Android4.2代碼的關於Input子系統的筆記。在這篇筆記中,只涉及Android相關的東西,關於Linux內核中對各類輸入設備的統一,在本文中不做說明。此外,因爲才疏學淺,文中不免有錯誤的地方,但願各位路過的大神可以予以指出。閒話少敘,先看一張我本身設計的圖,以下:android
這幅圖是爲了便於我的理解畫出的,裏面的註釋也比較明白,就再也不說明。本文就是以這幅圖爲基本的思路,簡述在Android4.2系統中和Input子系統的相關一些內容。如圖,本文將分爲如下幾個部分敘述:數組
(0)Input系統的啓動網絡
(1)InputReader的功能,以及執行的流程session
(2)InputDispatcher的功能,及執行流程數據結構
(3)Input子系統中的通訊方式是什麼?app
(4)應用程序是如何接收到並處理事件的socket
在開始敘述各部分的功能以前,咱們仍是先說說更個Input系統的前因後果,一方面可以知道Input系統從哪兒來,另外一方面能對整個系統有個大概的瞭解,使咱們不至於迷失在浩瀚的Android源碼中。在Android系統中一說到重要的服務,基本都是要從systemserver進程開始提及,由於他是Android世界的開拓者,建立了Android世界所須要個基礎。一樣,Input系統也是從systemserver中開始提及,首先建立一個InputManagerService對象,爲這個對象設置與WindowManagerService相關的回調函數,而後調用InputManagerService的start函數。ide
1 inputManager = new InputManagerService(context, wmHandler); 2 inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); 3 inputManager.start();
在InputManagerService中start方法會經過JNI調用,啓動Native層的InputReaderThread,InputDispatcherThread線程,從而開始Input系統的運行。InputReaderThread主要是執行和InputReader相關的內容,主要是從EventHub中讀取事件,預處理事件,然會是根據policy來處理此事件,最後發送一個消息到InputDispatcher中通知事件的產生。緊接着InputDispatcher會開始事件的分發,經過InputChannel把事件分發給WindowManager或者應用程序。說以一個事件的流程是從 Eventhub ---> InputReader ---> InputDispatcher ---> InputPublisher ---> InputChannel ---> InputConsumer ---> WindowManager or Application.這就是整個事件分發的大體流程。函數
由這個大體的流程開始,咱們逐步來解析Android系統Input的內容。從Input的啓動開始,也就是InputManagerService的建立和線程的啓動開始。先看InputManagerService的構造函數,代碼以下:oop
1 public InputManagerService(Context context, Handler handler) {//這裏的handler是WindowManagerService處理消息專用的線程,InputManagerService會把消息發送到這個線程中loop 2 this.mContext = context; 3 this.mHandler = new InputManagerHandler(handler.getLooper());//而和InputManagerService相關的消息的處理時在這個對象中完成的 4 5 mUseDevInputEventForAudioJack = 6 context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); 7 Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" 8 + mUseDevInputEventForAudioJack); 9 mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//經過JNI調用來啓動native層的input系統,而後把返回值存放在mPtr中 10 }
從代碼能夠看出,InputManagerService的構造是很簡單的,只是在最後經過JNI方法初始化了native層的Input系統。接下來咱們就看看在native層都作了些什麼,代碼以下:
1 static jint nativeInit(JNIEnv* env, jclass clazz, 2 jobject serviceObj, jobject contextObj, jobject messageQueueObj) { 3 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 4 if (messageQueue == NULL) { 5 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 6 return 0; 7 } 8 //這裏實例化了NativeInputManagerService的一個對象,使用的Java層的MessageQueue的Looper,意味着Java層消息和Native消息是在同一個MessageQueue中的 9 NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, 10 messageQueue->getLooper()); 11 im->incStrong(0); 12 return reinterpret_cast<jint>(im);//把新建的NativeInputManager強制轉換,返回給Java層 13 }
在native層初始化的時候,建立了一個名叫NativeInputMnager的對象,這個對象是很重要的,由於它主要負責和系統的其餘模塊交互,並且InputReader和InputDispatcher都是隻運行在Native層中,若是須要調用Java函數也是經過這個對象進行的,另外他實現了InputReaderPolicyInterface和InputDispatcherPolicyInterface,是一個重要的Policy。NativeInputManager在構造過程當中,完成了InputManager在native基本運行組件的建立,好比建立了EventHub對象,它是事件的Android系統的起源地,全部的事件都是它從驅動中讀取出來的;還建立了InputReaderThread線程用來執行InputReader的功能;InputDispatcherThread用來執行InputDispatcher的功能;同時也建立了InputManager來管理EventHub,InputReader,InputReaderThread,InputDispatcher,InputDispatcherThread這些Native運行的基本對象。這些對象的建立過程當中並無很是重要的調用,這裏略過代碼。不過要注意一點的是NativeInputManager是InputReaderPolicyInterface和InputDispatcherPolicyInterface的子類,所以在構造InputReader和InputDispatcher的時候要用到NativieInputManager對象。
在對象構建完成後,開始執行start方法,讓以前建立的這些對象運行起來。start方法也是比較簡單的,就是經過JNI調用讓native層的Input系統運行起來,而後在Java層把本身列入WatchDog的監視範圍內。以後定義下本身須要接受的外部通知等。這個過程看代碼的話,比較容易,再也不列出。那麼到這裏位置,整個Input系統就運行起來了,至於其中具體的功能咱們再逐步分析。這部份內容敘述完畢。
(1)InputReader的功能,以及執行的流程
從前面的內容咱們能夠知道,在InputManager的start方法被調用會,會執行兩個線程,分別是InputReaderThread和InputDispatcherThread,雖然它們的啓動在代碼上有前後之分,可是在實際執行過程當中是沒有前後的,因此先從哪一個線程開始解析Input系統不是很重要的。不過,我是按照從事件的產生到分發開始解析的,因此這裏我是選擇從InputReader開始。InputReader是Android系統中重要的部分,根據Android文檔中的描述,主要功能就是:(1) 從EventHub讀取事件,這些事件是元事件,即沒有通過加工或者僅僅是簡單加工的處理的事件;(2)把這些事件加工處理,生成inputEvent事件,這樣封裝以後的事件,能夠知足Android系統的一些需求;(3)把這些事件發送到事件監聽器,即QueuedInputListener,這個監聽器能夠把事件傳遞給InputDispatcher。下面咱們就從線程開始執行的地方一步一步分析這些功能的實現。既然要看InputReader的功能,我就從InputReader的構造函數提及。前面在說到構造InputManager的時候,就建立了InputReader,當時沒有介紹起功能和構造方法,咱們從這裏開始:
1 InputReader::InputReader(const sp<EventHubInterface>& eventHub, 2 const sp<InputReaderPolicyInterface>& policy, 3 const sp<InputListenerInterface>& listener) : 4 mContext(this), mEventHub(eventHub), mPolicy(policy), 5 mGlobalMetaState(0), mGeneration(1), 6 mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), 7 mConfigurationChangesToRefresh(0) { 8 mQueuedListener = new QueuedInputListener(listener);//在這裏建立了一個QueuedInputListener,注意其參數是listener是InputDispatcher 9 10 { // acquire lock 11 AutoMutex _l(mLock); 12 13 refreshConfigurationLocked(0); 14 updateGlobalMetaStateLocked(); 15 } // release lock 16 }
在InputReader建立的時候,這裏把InputDispatcher做爲參數傳遞進來,而後以InputDispatcher做爲參數構造出了QueuedInputListener對象。因此如今有這麼一個關係:InputReader持有一個QueuedInputListener,而QueuedInputListener持有InputDispatcher對象。接下來,咱們繼續以線程爲線索,分析咱們的代碼,接着看
1 bool InputReaderThread::threadLoop() { 2 mReader->loopOnce(); 3 return true; 4 }
在這裏補充一點內容: Android系統在Native層中實現了一個相似於Java中的線程對象,即C++中的Thread類。這個線程類有個特色就是,當線程開始執行後,不一直重複執行threadLoop方法,知道這個線程的強引用計數變爲零爲止。因此,這裏的threadLoop函數會不停地執行下去,也便是mReader->loopOnce()會循環執行下去,每循環一次就能從EventHub中讀取出若干事件。下面咱們就以一次循環過程爲例,分析此線程的執行,loopOnce的代碼以下:
1 void InputReader::loopOnce() { 2 int32_t oldGeneration; 3 int32_t timeoutMillis; 4 bool inputDevicesChanged = false; 5 Vector<InputDeviceInfo> inputDevices; 6 ... 7 //若是系統剛剛啓動,或者有新的設備加入的話,timeoutMillis通常爲0,意味着無需等待,能夠當即返回;timeoutMillis通常爲-1,意味着無限等待 8 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); 9 10 { 11 AutoMutex _l(mLock); 12 mReaderIsAliveCondition.broadcast(); 13 14 if (count) { 15 processEventsLocked(mEventBuffer, count);//開始處理讀取出來的元事件 16 } 17 18 ... 19 } 20 21 if (inputDevicesChanged) { 22 mPolicy->notifyInputDevicesChanged(inputDevices); 23 } 24 //把QueuedInputListener中的消息所有都開始處理 25 mQueuedListener->flush(); 26 }
整個方法的功能就是,從EventHub中讀取出若干事件,然會對這些事件進行預處理,然會把QueuedInputListener中的事件分發出去。這個方法中包含了InputReader的主要功能,因此此線程每循環一次,都會執行完成一次InputReader的主要功能。先說從EventHub讀取事件功能:
1.1 從EventHub獲取事件
先簡單介紹下EvenHub,這個類的主要功能就是主動監視Input驅動的變化,一旦有事件產生,就從產生事件相應的驅動中讀取出這個事件。實現這個監視驅動功能,是經過Linux提供的epoll機制來實現。epoll機制簡單地說就是高效地I/O多路複用機制,使用epoll_wait來監聽所須要的文件描述符的變化,關於epoll的介紹有不少文章,man中也有詳細的介紹。EventHub的主要功能是經過epoll_wait來實現的,因此EventHub所在的線程應該會阻塞在epoll_wait方法中,一直等到epoll_wait設置的超時時間。如今咱們開始看看EventHub的實現,在EventHub的構造函數中,創建了一個管道,並把這個管道的讀端和寫端的文件描述符添加到epoll的監視之下,以便於其餘的線程或者進程可以使EventHub所在的線程從epoll_wait的阻塞中返回。EventHub在建立完成以後,第一個被調用的方法就是getEvents,並且這個方法也是EventHub的主要功能,對於這個方法須要仔細分析,咱們把getEvents方法也分紅了三個部分去解析,分別是:打開設備部分;事件讀取部分;等待部分。這三個部分中,以事件的讀取部分爲重點。設備打開部分通常發生在Input系統創建的時候調用,因此在系統啓動完成,穩定以後,這部份內容應該不會再被執行的;而等待部分較爲簡單。不過這些做爲系統必不可少的部分,仍是要一一說明的,先說設備打開部分吧,代碼以下:
1 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { 2 ... 3 struct input_event readBuffer[bufferSize]; 4 //這是元事件指針,能夠指向一系列的事件,這些事件按照數組的方式存放的 5 RawEvent* event = buffer; 6 size_t capacity = bufferSize; 7 bool awoken = false; 8 for (;;) { 9 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 10 //mNeedToReopenDevices = false; mClosingDevices = 0;mNeedToSendFinishedDeviceScan = false;mOpeningDevices = 0 11 //mNeedToScanDevices = true 12 if (mNeedToScanDevices) { 13 mNeedToScanDevices = false; 14 scanDevicesLocked(); 15 mNeedToSendFinishedDeviceScan = true; 16 } 17 ...
EventHub對象在初始化的時候,有不少變量都已經賦值,因此我把代碼中判斷不成立的代碼塊暫時都拿掉了,只留下了在Input系統啓動時候會執行的內容,也就是scanDevicesLocked方法。在這個方法執行以後,確定會產生一些設備添加,移除之類的事件,到時候在一一分析。在這個方法中,使用了一個結構體叫RawEvent,使用這個結構體簡單地代表事件發生的基本信息,代碼以下:
struct RawEvent { nsecs_t when;//事件發生的時間,在getEvents中對於事件時間的處理也是值得關注的 int32_t deviceId;//產生這個事件對應的設備的ID,與具體的硬件無關,其數值和設備打開的順序有關 int32_t type;//事件的類型 int32_t code;//事件對應的事件碼 int32_t value;//事件的內容 };
RawEvent來自兩種,一種是在打開設備時本身賦值,不如設備的添加,移除等,這些事件對應的RawEvent都是getEvents本身賦值的,便於InputReader處理;還有一種是來自驅動的產生的事件,由驅動產生的這類事件,在內容中有其本身的定義的類型,就是input_event。 getEvents能夠根據input_event產生相應的RawEvent便於InputReader處理。這裏要額外說明一點的就是RawEvent的type,若是是由輸入設備產生的事件,那麼這個type是和輸入設備自己的特性相關的,下面列舉出Linux中支持的事件類型:
EV_SYN | 用於標識獨立的事件,這些獨立的事件時在時間或者空間上是能夠分離的,好比在多點觸摸中 |
EV_KEY | 用於標識按鍵,按鈕或者相似按鍵的設備狀態的變化 |
EV_REL | 用於描述 對於軸線相對變化量,如鼠標向左移動5個單位 |
EV_ABS | 用於描述 對於軸線的絕對變化量, 好比在觸摸屏上的觸摸點的座標 |
EV_SW | 標識二進制的開關狀態 |
EV_LED | 表示設備上的LED是開or關 |
EV_SND | 用於標識發送聲音到設備 |
EV_REP | 表示自動重複的設備 |
V_FF | 用於標識發送強制要回饋的命令到設備 |
EV_PWR | 對於Power鍵的一個特殊狀態或者切換輸入 |
EV_FF_STATUS | 用於收到須要強制回饋的設備狀態 |
EV_MSC | 若是不是這些已存在的狀態,那麼就用這個標識 |
這個表格來自於Linux內核文檔中的Document/input/event-codes.txt,若是以上有翻譯不恰當的地方,能夠去參考原文檔。上面這些類型是Linux支持的全部的事件類型,通常的一類設備能夠支持這些類型中的一個或幾個。
在Android系統中,經常使用的設備由觸摸屏,鍵盤或者鼠標等,這些設備通常是可以產生以下類型的事件:
多點觸屏 | 大可能是EV_ABS, EV_KEY, EV_SYN,有的還設置了EV_MSC |
鍵盤 | EV_KEY, EV_SW |
鼠標 | EV_REL, EV_KEY, EV_ABS |
這個表格僅僅是通常性而言,具體狀況還須要參考相應的設備驅動文件。這裏之因此介紹這些東西,是由於在InputReader在預處理這些事件的時候會使用type這個類型。瞭解了這些以後,繼續看EventHub是如何打開這些設備的。 EventHub是經過掃描/dev/input/目錄下全部可用的設備,而後逐一打開這些設備,打開這些設備過程當中,EventHub又作了一些Input系統必要的工做,好比構造Device對象,把這些設備加入到epoll的監視隊列中等,時間戳的設定等。在構造Device對象的時候,是經過InputDeviceIdentifier來構造的,主要思路就是經過ioctl函數從內容中讀取出一些必要的信息,而後把這些信息通過InputDeviceIdentifier存入Device中,而後再經過ioctl函數測試設備的屬性,把這些屬性信息也存入Device中。代碼以下:
1 status_t EventHub::openDeviceLocked(const char *devicePath) { 2 ... 3 InputDeviceIdentifier identifier; 4 5 // 獲取設備的名字,若是成功獲取到設備的名字,把它存入InputDeviceIdentifier中 6 if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { 7 //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); 8 } else { 9 buffer[sizeof(buffer) - 1] = '\0'; 10 identifier.name.setTo(buffer); 11 } 12 ... 13 14 //構造EventHub所須要的對象Device,這裏的fd是剛剛打開的設備的文件描述符 15 int32_t deviceId = mNextDeviceId++;//從這裏能夠看出,deviceId是與設備無關的,和打開順序有關 16 Device* device = new Device(fd, deviceId, String8(devicePath), identifier); 17 18 // 測試設備可以產生的事件的類型,這些事件類型在前文中已經說到過。這裏就是Android支持的事件類型,是Kernel的一個子集 19 ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); 20 ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); 21 ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); 22 ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); 23 ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); 24 ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask); 25 ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); 26 ... 27 //根據前面獲取到的設備屬性,檢測設備是鼠標,鍵盤,手柄等,而後把這些信息繼續存入Device 28 if (test_bit(BTN_MOUSE, device->keyBitmask) 29 && test_bit(REL_X, device->relBitmask) 30 && test_bit(REL_Y, device->relBitmask)) { 31 device->classes |= INPUT_DEVICE_CLASS_CURSOR; 32 } 33 ...
這部分代碼,把InputDeviceIdentifier轉化爲了Device,由於Device可以存儲更多的信息,是EventHub所須要的。在打開設備的時候對這些Device完成了初始化。而後就是把這些設備加入epoll的監視中,代碼以下:
1 epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)
如此以後,只要設備有輸入事件的產生,經過epoll就能從阻塞中返回。以後就是設置設備的硬件時鐘。在報告事件的時候,咱們要使用的時鐘是monotonic clock, 這時鐘的特色就是在每次開機的時候初始化爲0。事件發生時的時間戳在input系統中使用很是普遍,並且Input系統會假設事件的時間戳是monotonic的時間點。最後把這些設備添加到EventHub的一個Vector中,相似以下格式:
deviceId | Device* |
1 | Device* |
2 | Device* |
... | ... |
這個數組將會在EventHub中普遍地使用,常用的方式是經過deviceId獲取Device設備。到這裏,打開設備的工做已經完成,並且爲EventHub的工做建立了一些有用的變量和數組等。EventHub中的第一個功能,打開設備已經完成。接着咱們在看看事件等待部分,最後再說事件的讀取。其實事件的等待部分很簡單,主要的代碼就一行,以下:
epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
注意代碼中的最後一個參數timeoutMillis,前面已經說到過,通常來講這個參數是-1,意味着線程會在這個地方阻塞,無限等待下去,直到有事件的發生,而在新的設備加入的時候,這個值爲0,意味着能夠當即返回。因此,在系統啓動完成後,若是沒有事件發生的話,InputReaderThread線程會阻塞在這裏,一直等待事件的發生。最後,咱們看看事件的讀取部分,代碼以下:
1 bool deviceChanged = false; 2 while (mPendingEventIndex < mPendingEventCount) { 3 const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; 4 ...//這裏省略了對於其餘的epoll類型的處理。若是是EPOLLIN類型的事件,意味着epoll監視的文件描述符中有寫入事件,這類事件是輸入事件, 5 Device* device = mDevices.valueAt(deviceIndex); 6 if (eventItem.events & EPOLLIN) {//從產生事件的描述符中讀取出事件,放入readerBuffer 7 int32_t readSize = read(device->fd, readBuffer, 8 sizeof(struct input_event) * capacity); 9 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { 10 deviceChanged = true; 11 closeDeviceLocked(device); 12 } else if (readSize < 0) { 13 if (errno != EAGAIN && errno != EINTR) { 14 ALOGW("could not get event (errno=%d)", errno); 15 } 16 } else if ((readSize % sizeof(struct input_event)) != 0) { 17 ALOGE("could not get event (wrong size: %d)", readSize); 18 } else { 19 int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; 20 //在設備上產生的事件的個數 21 size_t count = size_t(readSize) / sizeof(struct input_event); 22 for (size_t i = 0; i < count; i++) { 23 const struct input_event& iev = readBuffer[i]; 24 ...//這裏省略了對於事件時間戳的設定,考慮的因素挺多,雖時間戳對於輸入事件很重要,可是不該該是本次討論的重點 25 event->when = now; 26 event->deviceId = deviceId; 27 event->type = iev.type; 28 event->code = iev.code; 29 event->value = iev.value; 30 event += 1; 31 } 32 capacity -= count; 33 if (capacity == 0) { 34 mPendingEventIndex -= 1; 35 break; 36 } 37 } 38 } 39 ... 40 }
其實這段代碼也是很是簡單的,基本過程就是,監視到有事件的產生,把事件讀取出來,不過這裏讀出的事件是input_event類型的,而後在逐個把input_event事件轉化爲InputReader須要的RawEvent類型的事件,放入InputReader提供給EventHub的數組中(經過getEvents參數傳遞進來的)。提及來很簡單,其實也很簡單。上面這些代碼就是讀取事件的核心部分。總結一下,EventHub負責打開/dev/input/目錄下的全部設備,而後爲每個設備建立一個Device,並把這個Device放入EventHub所定義的數組們Device中。以後,就是把這個設備歸入監視範圍。而後就是開始等待事件的發生,一旦有事件發生,就從產生事件的設備中讀取出這些設備,把這些事件轉化爲RawEvent類型放入InputReader提供的事件數組中,以後返回。到這裏,從EventHub獲取事件就結束了。
1.2 InputReader對元事件的處理
由上節的內容,咱們知道,從EventHub得到的事件有兩種,一種是設備添加,移除類的;另外一種是由輸入設備產生的事件。InputReader在處理這兩類事件稍微有點不同。先看設備添加類型的事件,這些添加設備事件的處理,爲InputReader的工做打下了基礎,由於InputReader能夠根據添加的設備定義一些數據結構,爲之後處理由此設備產生的事件打下基礎。接着咱們從代碼開始看看InputReader對於元事件的處理:
1 void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { 2 for (const RawEvent* rawEvent = rawEvents; count;) { 3 int32_t type = rawEvent->type; 4 size_t batchSize = 1; 5 if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { 6 int32_t deviceId = rawEvent->deviceId; 7 while (batchSize < count) { 8 if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT 9 || rawEvent[batchSize].deviceId != deviceId) { 10 break; 11 } 12 batchSize += 1; 13 } 14 //有輸入設備產生的事件,在這個方法中處理 15 processEventsForDeviceLocked(deviceId, rawEvent, batchSize); 16 } else { 17 switch (rawEvent->type) {//設備添加類的事件在這裏處理 18 case EventHubInterface::DEVICE_ADDED: 19 addDeviceLocked(rawEvent->when, rawEvent->deviceId);//這個方法中建立了InputReader所必須的一些數據結構 20 break; 21 case EventHubInterface::DEVICE_REMOVED: 22 removeDeviceLocked(rawEvent->when, rawEvent->deviceId); 23 break; 24 case EventHubInterface::FINISHED_DEVICE_SCAN: 25 handleConfigurationChangedLocked(rawEvent->when); 26 break; 27 default: 28 ALOG_ASSERT(false); // can't happen 29 break; 30 } 31 } 32 count -= batchSize; 33 rawEvent += batchSize; 34 } 35 }
先從設備添加類的事件提及,看看在添加設備的時候,都建立了那些數據結構。對於addDeviceLocked的源碼,這裏就不列舉出來,主要說說在InputReader在功能實現時用的變量有那些,分別是是InputDevice,InputMapper。InputDevice表明輸入設備的一個狀態;InputMapper是某一類事件是如何處理的;二者之間的關係是,一個InputDevice能夠產生多種類型的事件,所以他能夠對應多個InputMapper。另外,在InputReader中也保存了一個vector來保存InputDevice,這個Vector的名字也叫mDevices,和EventHub中的mDevices相似,不過保存的內容有些不一樣。在InputReader的mDevices中保存的<id, InputDevice*>,而在EventHub中保存的是<id, Device*>,不過二者的id是一致的,並且每一個InputDevice都是經過Devices來構造的。可以完成加工RawEvent工做的仍是經過不一樣的InputMapper來完成的,這些InputMapper根據Android系統支持的類型分紅了一下幾類,
InputMapper類型 | 可以處理的事件的類型 |
SwitchInputMapper | EV_SW, EV_SYN |
KeyboardInputMapper | EV_KEY, EV_SYN, EV_MSC |
CursorInputMapper | EV_KEY, EV_SYN, EV_REL |
TouchInputMapper | EV_KEY, EV_SYN, EV_REL |
SingleTouchInputMapper | EV_KEY, EV_SYN, EV_REL, EV_ABS |
MultiTouchInputMapper | EV_KEY, EV_SYN, EV_REL, EV_ABS |
JoyStickInputMapper | EV_ABS, EV_SYN |
VibratorInputMapper | -- |
這裏就基本完成了對於添加設備類的事件的處理,接下來就看是分析對於輸入設備產生的元事件的處理。對於輸入事件的處理主要是經過方法processEventsForDeviceLocked進行的,在這個方法執行以前,已經找到了產生這個事件的輸入設備了,而後把輸入設備做爲參數傳遞進去,processEventsForDeviceLocked方法根據deviceId找到相應的InputDevice,而後調用InputDevice的process方法進行處理這個事件。下面,結合InputDevice的process方法的這段代碼,咱們一塊兒看看輸入事件是如何處理的,代碼以下:
1 void InputDevice::process(const RawEvent* rawEvents, size_t count) { 2 size_t numMappers = mMappers.size(); 3 for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { 4 ...//省略了一些與判斷的處理,留下核心部分的代碼 5 for (size_t i = 0; i < numMappers; i++) { 6 InputMapper* mapper = mMappers[i]; 7 mapper->process(rawEvent);//讓各個InputMapper去處理元事件,注意參數仍是RawEvent類型 8 } 9 } 10 11 }
在這個方法中,注意有內外兩個循環,外循環是逐一取出元事件,內循環是讓每個InputMapper都處理這個事件。之因此讓每個InputMapper都進行處理元事件,而不是隻要對應的InputMapper去處理,是由於擔憂只讓對應的InputMapper處理元事件會產生反作用,好比For example, joystick movement events and gamepad button presses are handled by different mappers but they should be dispatched in the order received. 對於每個InputMapper都要處理元事件,咱們不作一一分析,僅僅拿出典型的鍵盤輸入事件分析。處理過程以下:
1 void KeyboardInputMapper::process(const RawEvent* rawEvent) { 2 switch (rawEvent->type) { 3 case EV_KEY: { 4 int32_t scanCode = rawEvent->code; 5 int32_t usageCode = mCurrentHidUsage; 6 mCurrentHidUsage = 0; 7 8 if (isKeyboardOrGamepadKey(scanCode)) { 9 int32_t keyCode; 10 uint32_t flags; 11 if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) { 12 keyCode = AKEYCODE_UNKNOWN; 13 flags = 0; 14 } 15 processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags); 16 } 17 break; 18 } 19 ...//省略了對於其餘事件類型EV_SYN, EV_MSC的處理代碼 20 } 21 22 23 void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, 24 int32_t scanCode, uint32_t policyFlags) { 25 ...//省略了對於元事件處理過程的代碼,主要就是發生事件,事件代碼,掃描碼,是按下仍是彈起, 26 //總之,用於構建下面NotifyKeyArgs的參數大都是在這裏獲取的。 27 NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags, 28 down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 29 AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime); 30 getListener()->notifyKey(&args); 31 }
這是InputReader對於元事件處理的過程。在處理完成後,在最後調用了一個重要的方法getListener()->notifyKey(&args)方法。在InputReader處理各類元事件的時候,基本過程都是這樣的,把元事件中的各項信息構建一個NotifyArgs,而後經過QueuedInputListener來通知InputDispatcher。由此,InputReader的處理過程開始進入了和InputDispatcher交互的階段。其實在QueuedInputListener中對於notifyKey的實現很是簡單,僅僅是把這些事件的參數壓入隊列而已,並無作太多的操做就返回了。
1.3 InputReader把事件發送到InputDispatcher
前面咱們已經知道了,InputReader把元事件處理完畢後,構造了一個NotifyArgs,並把這個對象壓入了QueuedInputListener的隊列中,而後就返回了。當時咱們並不知道如何把這些隊列中的事件發送的InputDispatcher中的。這裏,就給出了這個過程。InputReader調用QueuedInputListener的flush方法,把QueuedInputListener隊列中的全部事件都發送到InputDispatcher中。下面咱們就分析這個過程,從QueuedInputListener的flush方法提及,代碼以下:
1 void QueuedInputListener::flush() { 2 size_t count = mArgsQueue.size();//前面,咱們就是把NotifyArgs放入了mArgsQueue中 3 for (size_t i = 0; i < count; i++) { 4 NotifyArgs* args = mArgsQueue[i]; 5 args->notify(mInnerListener);//逐個取出NotifyArgs,而後調用notify方法,注意這裏面的參數是mInnerListener,是InputDispatcher 6 delete args; 7 } 8 mArgsQueue.clear(); 9 }
這裏從隊列中逐個取出NotifyArgs,而後調用他們的notify方法。在QueuedInputListener建立的時候,咱們傳入構造函數的的參數是一個InputDispatcher,在這裏就使用到了,把這個InputDispatcher做爲參數向下傳遞。在NotifyArgs的notify方法中,基本都相似於
1 62void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { 2 63 listener->notifyKey(this);//調用InputDispatcher的對應的方法。 3 64}
到這裏,咱們對於InputReader的功能的分析就完成了。總結一下,基本過程說就是:InputReader從EventHub中讀取出來元事件,預處理加工這些元事件成爲NotifyArgs,而後經過QueuedInputListener把他們通知給InputDispatcher。整個Input的流程圖太大,在這裏顯示不徹底。如今僅僅拿出,和InputReader功能相關的部分的流程圖,圖中是以一個鍵盤事件的處理過程。圖以下:
2. InputDispatcher的功能和流程
在開始介紹InputDispatcher的功能以前,先看看Android文檔對於其功能的描述:把輸入事件發送到他的目標中去。他的目標多是應用程序,也多是WindowManagerService。若是是應用程序的話,能夠經過registerInputChannel來定義輸入事件的目標。咱們已經瞭解InputDispatcher的惟一一個功能就是分發事件。知道了其功能以後,咱們就開始分析InputDispatcher是如何實現這些功能的吧。先看他的構造函數,InputDispatcher建立了一個Looper,代碼以下:
1 mLooper = new Looper(false);
這意味着,InputDispatcher有本身的Looper,沒有和別人共用,信息也是本身在循環的。這個Looper是native層的Looper,由C++代碼實現。在構建Looper過程當中,新建了一個管道,這個管道僅僅起到了喚醒Looper,讓其能從阻塞等待中返回。Looper中建立的管道是實現Looper功能的重要的方式,是通用的,不是僅僅爲了InputDispatcher準備的。看完構造函數以後,咱們接着分析InputDispatcher的功能,接着上節中的QueuedInputListener通知InputDispatcher有新的按鍵事件提及。這裏仍是接着上面的以按鍵的處理,接着看InputDispatcher是如何實現分發的,代碼以下:
1 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 2 ... 3 KeyEvent event;//在這裏經過傳遞進來的NotifyArgs爲參數,構建KeyEvent 4 event.initialize(args->deviceId, args->source, args->action, 5 flags, args->keyCode, args->scanCode, metaState, 0, 6 args->downTime, args->eventTime); 7 //經過NativeInputManager把這個KeyEvent最終傳遞給WindowManagerService去處理 8 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); 9 ... 10 bool needWake; 11 ... 12 int32_t repeatCount = 0;//這下面構建KeyEntry 13 KeyEntry* newEntry = new KeyEntry(args->eventTime, 14 args->deviceId, args->source, policyFlags, 15 args->action, flags, args->keyCode, args->scanCode, 16 metaState, repeatCount, args->downTime); 17 18 needWake = enqueueInboundEventLocked(newEntry); 19 mLock.unlock(); 20 21 if (needWake) {//喚醒等待Looper 22 mLooper->wake(); 23 } 24 }
咱們先從代碼中的line 8開始,這行代碼的意思是,把KeyEvent發送出去,至於目的地是哪兒,在InputDispatcherPolicy中會有決定。是NativeInputManager實現了這個Policy,因此代碼的執行會進入NativeInputManager中。
事件在入隊前(before enqueue)的處理
在文章的前面已經說到過,NativeInputManager負責和系統的其餘模塊交互--是其功能之一。把這個KeyEvent傳遞到NativeInputManager以後,繼續分發,最終會把這個KeyEvent傳遞到PhoneWindowManager中去處理這個事件,傳遞過程以下:NativeInputManager->interceptKeyBeforeQueueing ----> InputManagerService.interceptKeyBeforeQueueing ----> InputMonitor.interceptKeyBeforeQueueing ----> PhoneWindowManager.interceptKeyBeforeQueueing.大體過程是這樣的,具體細節再也不贅述。在傳遞過程當中是跨線程的。經過這一系列的方法的名字能夠看出,是在事件進入InputDispatcher的隊列以前,進行的一些處理。在PhoneWindowManager處理事件以後,會有一個返回值來標記這一事件處理的結果是怎樣的,爲後面的事件進入隊列作準備。在PhoneWindowManager對事件進行前期的攔截處理過程時,通常首先把事件都標記上PASS_TO_USER,即這個事件要交給應用程序去處理,可是在處理過程當中決定,有些事件是不必傳遞給應用程序的,好比:在經過過程當中按下音量相關的事件,掛斷電話的事件,power鍵的處理,以及撥打電話的事件。這些事件的處理結果都是沒必要傳遞到應用程序的,這個結果最爲返回值,最終會一步一步地返回到NativeInputManager中,這個返回值會做爲NativeInputManager的policyFlags的一部分,供InputDispatcher使用。在PhoneWindowManager對事件處理完成後,纔會把這個事件構形成爲一個形式爲EventEntry放入隊列。到這裏,咱們的工做仍在InputReaderThread的線程中,雖然是對InputDispatcher的操做。接下來纔是真正進入InputDispatcherTread線程對InputDispatcher操做。經過InputDispatcher的mLooper的wake方法,喚醒InputDispatcherThread線程。關於Looper如何在wake時是如何經過管道的方式去實現的,這個過程應該放在一篇單獨的文章中詳細地去說明,在之後的文章中,我會說到Looper在native實現時的一些特色的。這裏,咱們知道InputDispatcherThread線程被喚醒了。若是你已忘記InputDispatcherThread線程是什麼時候被阻塞,那就回頭再從新看看吧。學習別人的思路就是這樣,反覆回頭看,才能不至於迷失在別人的思惟中。而後就開始執行InputDispatcher的threadLoop函數,以後就調用InputDispatcher的dispatchOnce方法,代碼以下:
1 void InputDispatcher::dispatchOnce() { 2 nsecs_t nextWakeupTime = LONG_LONG_MAX;//應該是64位二進制所能表示的最大值,大概是2^63-1,即9223372036854775807 3 { 4 AutoMutex _l(mLock); 5 mDispatcherIsAliveCondition.broadcast(); 6 //若是沒有等待執行的命令的話,就開始一次循環分發。在循環過程當中,可能會有一些命令產生。這裏的命令大概是模式設計中的:命令模式吧 7 if (!haveCommandsLocked()) { 8 dispatchOnceInnerLocked(&nextWakeupTime); 9 } 10 //若是任何等待執行的命令的話,那麼就執行這些命令;假若有命令已經執行了,那麼下次poll的時候直接喚醒 11 if (runCommandsLockedInterruptible()) { 12 nextWakeupTime = LONG_LONG_MIN;//#define LONG_LONG_MIN (-__LONG_LONG_MAX__-1LL), 即-9223372036854775808 13 } 14 } // release lock 15 16 nsecs_t currentTime = now(); 17 int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime); 18 mLooper->pollOnce(timeoutMillis); 19 }
InputDispatcher的主要功能就在這段代碼中,這是個輪廓。要想知道具體的功能的實現,還要須要逐步分析下去。先看line7和line8中的代碼,若是是一次正常的分發循環(dispatch loop)的話,應該是沒有等待執行的命令。爲何會沒有等待執行的命令,在後面會說到緣由,先不要着急。因此接下就開始dispatchOnceInnerLocke方法,從這個方法的名字能夠看出,這應該是功能的核心實現部分。看其代碼是如何實現的:
1 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { 2 nsecs_t currentTime = now(); 3 //若是等待處理的事件不存在的話 4 if (! mPendingEvent) { 5 if (mInboundQueue.isEmpty()) { 6 ...//省略了,當等待處理事件不存在且事件隊列爲空的時候的處理 7 } else {//從事件隊列的頭部取出一個事件 8 mPendingEvent = mInboundQueue.dequeueAtHead(); 9 traceInboundQueueLengthLocked(); 10 } 11 if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { 12 //通知某些Activity一些事件的發生,經過這個方法的名字能夠聯想一下,一些社交網站中的「捅一下」應用,或者QQ中的震動窗口功能, 13 pokeUserActivityLocked(mPendingEvent);//這個方法的功能就相似於那些做用。只不過這裏主要是用來「捅一下」PowerManagerService的 14 } 15 16 // Get ready to dispatch the event. 17 resetANRTimeoutsLocked(); 18 } 19 //如今咱們有事件須要開始處理了 20 ALOG_ASSERT(mPendingEvent != NULL); 21 bool done = false; 22 DropReason dropReason = DROP_REASON_NOT_DROPPED;//在開始處理以前,全部的事件都沒必要丟棄 23 if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { 24 dropReason = DROP_REASON_POLICY; 25 } else if (!mDispatchEnabled) { 26 dropReason = DROP_REASON_DISABLED; 27 } 28 29 if (mNextUnblockedEvent == mPendingEvent) { 30 mNextUnblockedEvent = NULL; 31 } 32 33 switch (mPendingEvent->type) { 34 ...//省略了對於config change類別的事件的處理 35 ...//省略了對於設備重置事件的處理 36 case EventEntry::TYPE_KEY: { 37 KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); 38 if (isAppSwitchDue) {//下面這些內容,是對於事件是否須要丟棄的分析 39 if (isAppSwitchKeyEventLocked(typedEntry)) { 40 resetPendingAppSwitchLocked(true); 41 isAppSwitchDue = false; 42 } else if (dropReason == DROP_REASON_NOT_DROPPED) { 43 dropReason = DROP_REASON_APP_SWITCH; 44 } 45 } 46 if (dropReason == DROP_REASON_NOT_DROPPED 47 && isStaleEventLocked(currentTime, typedEntry)) { 48 dropReason = DROP_REASON_STALE; 49 } 50 if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { 51 dropReason = DROP_REASON_BLOCKED; 52 }//不管事件是否要被丟棄,都要通過以下的處理 53 done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); 54 break; 55 } 56 ...//省略了對於motion事件的處理 57 } 58 ... 59 }
這個方法中的大部分功能都已經在代碼中註釋了,主要就是取出事件,分析是否須要丟棄,而後就是開始按照類型分發事件,咱們假設的是按鍵事件,因此接下來就是調用dispatchKeyLocked方法來分發。
1 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, 2 DropReason* dropReason, nsecs_t* nextWakeupTime) { 3 if (! entry->dispatchInProgress) { 4 ...//省略了對於重複事件在各類狀況下的處理 5 } 6 7 ...//在入隊列以前,對於事件有個一次intercept,這裏是對事件的intercept結果的處理 8 Vector<InputTarget> inputTargets; 9 int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, 10 entry, inputTargets, nextWakeupTime);//尋找事件發送到的目標窗口 11 // 分發事件 12 dispatchEventLocked(currentTime, entry, inputTargets); 13 return true; 14 }
這個方法中主要就是尋找到事件應該分發到的目標,多是應用窗口.這個目標應用的窗口尋找與應用程序啓動時設置到窗口有關。在下一小節中會說到這個窗口是如何找到的。其代碼不是很複雜,本身看看的話也很容易可以明白。其餘的內容在上面的註釋中也有說明。下面仍是將注意力集中在事件分發上,注意這裏傳入dispatchEventLocked的參數中inputTargets是複數,也就是說可能有多個目標。因此在方法dispatchEventLocked中就是根據每個target對應的inputChannel找到connection,而後 prepareDispatchCycleLocked使用這個connection把事件逐個分發到target中。 在prepareDispatchCycleLocked方法中,主要就是根據事件是否能夠分割,分別把事件放入隊列。在入隊列的以後,InputPublisher的發佈事件的隊列就再也不爲空,而後會調用 startDispatchCycleLocked方法,經過InputPublisher開始發佈事件。大體過程如此,爲了減小篇幅,這裏就再也不列出代碼了。流程圖以下:
整個的流程圖太大了,不太方便,這裏僅僅是其中的一部分。說明一點:圖中Looper到InputDispatcher中的dispatcherOnce不是調用關係,只是Looper把其所在的進程即InputDispatcherThread線程給喚醒,因此開始執行dispatchOnce。這裏到最後就是調用InputPublisher的publishKeyEvent方法,把事件發佈出去。在前面咱們說到過這麼一個問題,等待執行的命令爲何在一次正常的事件分發以後應該爲空?這些命令產生的地方分別在pokeUserActivity方法中, 和dispatchKeyLocked中等等在使用postCommand把命令放入隊列的地方。在上面這個過程執行完畢後,會返回到dispatchOnce方法中,接着往下執行,也就是執行代碼:
runCommandsLockedInterruptible()
也就是前面dispatchOnce方法的line 11. 這個方法的功能就是執行以前放入命令隊列的命令。具體的代碼再也不列出。到這裏,關於InputDispatcher的功能--惟一的一個功能--事件分發,就算介紹完了。
3 通訊方式
從這節開始介紹Input子系統是如何實現通訊的。其實InputReaderThread與InputDispatcherThread之間, InputDispatcherThread和WindowManagerService所在線程之間的通訊是相對簡單的,由於他們在相同的進程---systemServer中,所以能夠經過使用同一個對象就能夠完成通訊。這裏就很少作介紹。主要是分析InputDispatcherThread與應用程序之間傳遞事件時的通訊----socket通訊,以及Input和應用程序是如何利用socket方式完成事件的傳遞的。
在開始以前,仍是再續點閒話吧,要否則直接開始下面的分析,會讓人以爲很突兀。我之因此能找到思路從下面的這個節點分析,是由於在寫這篇文章以前,我已經對Input系統有了大概的瞭解了,對於其中的通訊方式也有了瞭解的。從通訊方式的創建,反推一步一步地找到了ViewRootImpl中的。在文章中沒有按照我尋找線索的方式去寫,由於我以爲那麼寫的話有點混亂,並且要時刻保持緊張的心態去分析,太累。因此才能這麼開始的,但願可以得到理解。在每一個Activity建立的時候,都會擁有其相應的ViewRootImpl。這個知識點在網絡上不少文章分析Activity的啓動過程當中都會詳細描述的,這裏再也不贅述。ViewRootImpl就表明一個Activity創建可以接收事件的渠道。這個創建過程在ViewRootImpl的setView中。在setView中的代碼不少,功能也須要仔細分析,這裏僅僅列出和Input相關的代碼,以下:
1 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { 2 synchronized (this) { 3 if (mView == null) { 4 //若是這個Window的屬性中設置了再也不須要InputChannel,那麼就能夠不用建立InputChannel。 5 //咱們是須要一個InputChannel的。 6 if ((mWindowAttributes.inputFeatures 7 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 8 mInputChannel = new InputChannel(); 9 } 10 try { 11 mOrigWindowType = mWindowAttributes.type; 12 mAttachInfo.mRecomputeGlobalAttributes = true; 13 collectViewAttributes(); 14 //這裏是把InputChannel最終傳遞到WindowManagerService中,用於傳遞渠道的創建 15 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, 16 getHostVisibility(), mDisplay.getDisplayId(), 17 mAttachInfo.mContentInsets, mInputChannel); 18 } 19 ... 20 //DecorView是RootViewSurfaceTaker的一個實例, 21 if (view instanceof RootViewSurfaceTaker) { 22 //雖然這行代碼會被執行,可是獲得的最終值仍是null。在整個代碼中,我並無找到InputQueueCallback對象建立的地方 23 mInputQueueCallback = 24 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); 25 } 26 if (mInputChannel != null) { 27 if (mInputQueueCallback != null) { 28 mInputQueue = new InputQueue(mInputChannel); 29 mInputQueueCallback.onInputQueueCreated(mInputQueue); 30 } else { 31 //這裏建立了一個WindowInputEventReceiver,注意參數是前面建立的InputChannel和本Activity所在線程的Looper, 32 mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, 33 Looper.myLooper()); 34 } 35 } 36 ... 37 } 38 } 39 }
這段代碼就是咱們分析通訊機制在應用端創建的輪廓。後面的大部份內容都是基於這段代碼分析進行的,只不過是這段代碼的層層深刻而已。在以前,咱們一直沒有介紹在事件傳遞中一個重要的類InputChannel,這裏就詳細說明下。在Native層的InputChannel就是一個通道,僅僅是一個通道,僅僅具備通訊功能,不包含其餘的。至於從數據流動方向,與InputChannel無關。數據流向是有InputPublisher和InputConsumer在組合了InputChannel後決定的。先看在代碼line 8中,建立一個InputChannel實例,它是一個Java對象,經過它的構造函數能夠看出,只是建立了一個對象,並無進行任何實例化的操做。以後,就是把這個對象做爲參數傳遞到了WindowManagerService中,有addWindow來使用。把InputChannel由應用程序傳遞到WindowManageService的過程,涉及到的是Binder通訊,不是文章的重點,很少說。須要知道的是,mWindowSession.addToDisplay最後會傳遞到WindowManagerService的addWindow方法。經過代碼看看InputChannel是如何使用的,代碼以下:
1 public int addWindow(Session session, IWindow client, int seq, 2 WindowManager.LayoutParams attrs, int viewVisibility, int displayId, 3 Rect outContentInsets, InputChannel outInputChannel) { 4 ... 5 if (outInputChannel != null && (attrs.inputFeatures 6 & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { 7 //這個名字是根據對象的hashcode和窗口的一些屬性轉化爲字符串後創建的。 8 String name = win.makeInputChannelName(); 9 InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); 10 win.setInputChannel(inputChannels[0]); 11 inputChannels[1].transferTo(outInputChannel); 12 mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); 13 } 14 ... 15 }