<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 189,comments - 115,trackbacks - 0
    Android 4.0 事件輸入(Event Input)系統(tǒng)http://blog.csdn.net/myarrow/article/details/7091061

     1. TouchScreen功能在Android4.0下不工作

           原來在Android2.3.5下能正常工作的TouchScreen功能,移植到Android 4.0就不能正常工作了。憑直覺,Android4.0肯定有鬼。真是不看不知道,一看嚇一跳。在Android 4.0中,Event Input地位提高了,你看看,在Adroid2.3.5中,它在frameworks/base/libs/ui之下,在Android4.0中,它在frameworks/base/services/input之下,看到?jīng)]有,它有了自己的地位,就像在Kernel中一樣,有自己門戶了。

          再看看代碼,變化也太大了,當(dāng)然TouchScreen不能工作,首先自然會(huì)看接口部分代碼。首先看它是如何打開設(shè)備的,查看函數(shù)EventHub::openDeviceLocked,看看其代碼,大部分還是很熟悉的,但仔細(xì)一看多了一個(gè)下面的東東:

    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

          由于升級(jí)到Android4.0時(shí),Kernel還是2.6.35,并沒有進(jìn)行升級(jí)。既然需要EVIOCGPROP,就就看看evdev.c中的ioctl函數(shù)是否支持此功能。一看不支持,再看看Kernel3.0.8<這個(gè)Kernel版本與Android4.0是一伙的>,我的乖乖,它已經(jīng)支持了此功能,詳見evdev.c中函數(shù)evdev_do_ioctl,這個(gè)寫得2.6.35中的友好多了,分別處理:固定長(zhǎng)度命令、單個(gè)可變長(zhǎng)度命令和多個(gè)可變長(zhǎng)度命令。

          對(duì)于為什么我的TouchScreen在Android4.0不工作,答案顯而易見,我用的Kernel版本不對(duì),當(dāng)然移植到Android4.0對(duì)應(yīng)的Kernel(Kernel3.0.8)時(shí),TouchScreen驅(qū)動(dòng)本身也需要修改,因?yàn)閕nput_dev變化也比較大,比如增加了propbit字段,以供處理上面的ioctl時(shí)使用。

     

    2. Android 4.0如何管理各種驅(qū)動(dòng)設(shè)備

           正是由于遇到上面的問題,才促使自己對(duì)Event Input進(jìn)行深入了解。因?yàn)轵唑腰c(diǎn)水不是小弟的性格。

           這個(gè)年代干啥都有什么經(jīng)理,小弟之類的。比如去飯店吃飯,吃到小強(qiáng)了,總是會(huì)大吼一聲,經(jīng)理,過來看看,然后談打折或賠償?shù)膯栴}。可見經(jīng)理是不可缺少的,要不然我們找誰來維權(quán)啊!

           前面談到的EventHub,這個(gè)一看就是一個(gè)做實(shí)事的,肯定不是領(lǐng)導(dǎo),哪它的領(lǐng)導(dǎo)是誰呢? 哪我們就從以下幾方面來分析此問題:

           1)每個(gè)功能模塊是怎么產(chǎn)生的?

           2)讀取設(shè)備輸入流程?

           3)事件分發(fā)流程?

    3. 各個(gè)功能模塊是怎么產(chǎn)生的?

          先介紹一下每個(gè)模塊的工作職責(zé):EventHub, InputReader, InputManager...

    3.1 模塊功能

    3.1.1 EventHub

            它是系統(tǒng)中所有事件的中央處理站。它管理所有系統(tǒng)中可以識(shí)別的輸入設(shè)備的輸入事件,此外,當(dāng)設(shè)備增加或刪除時(shí),EventHub將產(chǎn)生相應(yīng)的輸入事件給系統(tǒng)。

            EventHub通過getEvents函數(shù),給系統(tǒng)提供一個(gè)輸入事件流。它也支持查詢輸入設(shè)備當(dāng)前的狀態(tài)(如哪些鍵當(dāng)前被按下)。而且EventHub還跟蹤每個(gè)輸入調(diào)入的能力,比如輸入設(shè)備的類別,輸入設(shè)備支持哪些按鍵。 

    3.1.2 InputReader

          InputReader從EventHub中讀取原始事件數(shù)據(jù)(RawEvent),并由各個(gè)InputMapper處理之后輸入對(duì)應(yīng)的input listener.

          InputReader擁有一個(gè)InputMapper集合。它做的大部分工作在InputReader線程中完成,但是InputReader可以接受任意線程的查詢。為了可管理性,InputReader使用一個(gè)簡(jiǎn)單的Mutex來保護(hù)它的狀態(tài)。

         InputReader擁有一個(gè)EventHub對(duì)象,但這個(gè)對(duì)象不是它創(chuàng)建的,而是在創(chuàng)建InputReader時(shí)作為參數(shù)傳入的。

    3.1.3 InputDispatcher

         InputDispatcher負(fù)責(zé)把事件分發(fā)給輸入目標(biāo),其中的一些功能(如識(shí)別輸入目標(biāo))由獨(dú)立的policy對(duì)象控制。

     

    3.1.4 InputManager

         InputManager是系統(tǒng)事件處理的核心,它雖然不做具體的事,但管理工作還是要做的,比如接受我們客戶的投訴和索賠要求,或者老板的出所筒。

         InputManager使用兩個(gè)線程:

         1)InputReaderThread叫做"InputReader"線程,它負(fù)責(zé)讀取并預(yù)處理RawEvent,applies policy并且把消息送入DispatcherThead管理的隊(duì)列中。

         2)InputDispatcherThread叫做"InputDispatcher"線程,它在隊(duì)列上等待新的輸入事件,并且異步地把這些事件分發(fā)給應(yīng)用程序。

         InputReaderThread類與InputDispatcherThread類不共享內(nèi)部狀態(tài),所有的通信都是單向的,從InputReaderThread到InputDispatcherThread。兩個(gè)類可以通過InputDispatchPolicy進(jìn)行交互。

         InputManager類從不與Java交互,而InputDispatchPolicy負(fù)責(zé)執(zhí)行所有與系統(tǒng)的外部交互,包括調(diào)用DVM業(yè)務(wù)。

    3.2 創(chuàng)建流程

    1)在android_server_InputManager_nativeInit中創(chuàng)建NativeInputManager對(duì)象,并保存到gNativeInputManager中;

    2)在創(chuàng)建NativeInputManager對(duì)象時(shí),它會(huì)創(chuàng)建EventHub對(duì)象<且創(chuàng)建是其成員mNeedToScanDevices的值為true>,然后把剛創(chuàng)建的EventHub對(duì)象作為參數(shù)創(chuàng)建InputManager對(duì)象;

    3)在創(chuàng)建InputManager對(duì)象時(shí),創(chuàng)建InputReader對(duì)象,然后把它作為參數(shù)創(chuàng)建InputReaderThread;創(chuàng)建InputDispatcher對(duì)象,然后把它作為參數(shù)創(chuàng)建InputDispatcherThread對(duì)象;(注:以上兩個(gè)線程對(duì)象都有自己的threadLoop函數(shù),它將在Thread::_threadLoop中被調(diào)用,這個(gè)Thread::_threadLoop是線程入口函數(shù),線程在Thread::run中被真正地創(chuàng)建

    4.1)創(chuàng)建InputReader對(duì)象

    4.1.1)把EventHub、readerPolicy<實(shí)質(zhì)為NativeInputManager對(duì)象>和創(chuàng)建的InputDispatcher對(duì)象作為參數(shù)創(chuàng)建InputReader對(duì)象:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

    4.1.2)在創(chuàng)建InputReader時(shí), 保存EventHub對(duì)象到mEventHub中,并創(chuàng)建QueuedInputListener對(duì)象并保存在mQueuedListener中

    4.2)創(chuàng)建InputDispatcher對(duì)象

    4.2.1)把傳入的參數(shù)dispatcherPolicy<實(shí)質(zhì)為NativeInputManager對(duì)象>作為參數(shù)創(chuàng)建InputDispatcher對(duì)象:mDispatcher = new InputDispatcher(dispatcherPolicy);

    4.2.1)在創(chuàng)建InputDispatcher時(shí),創(chuàng)建了一個(gè)looper對(duì)象:mLooper = new Looper(false);

    3.3 啟動(dòng)流程

    1)在android_server_InputManager_nativeStart中調(diào)用InputManager::start,代碼如下:

    result = gNativeInputManager->getInputManager()->start();

    2)在InputManager::start中,調(diào)用mDispatcherThread->run和mReaderThread->run,代碼如下:

    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

    3)在上面的Thread::run中,調(diào)用createThreadEtc函數(shù),并以Thread::_threadLoop作為入口函數(shù),以上面的mDispatcherThread或mReaderThread作為userdata創(chuàng)建線程

    4)至此InputReader線程和InputDispatcher線程都已經(jīng)工作,詳細(xì)信息見Thread::_threadLoop,在此函數(shù)中它將調(diào)用mDispatcherThread或mReaderThread的threadLoop函數(shù)來做真正的事

    5.1)mReaderThread->threadLoop

    bool InputReaderThread::threadLoop() {
        mReader->loopOnce();
        return true;
    }

    5.2)mDispatcherThread->threadLoop

    bool InputDispatcherThread::threadLoop() {
        mDispatcher->dispatchOnce();
        return true;

     

    3.4 EventInput對(duì)象關(guān)系圖 

     

    4. 設(shè)備操作流程

    從EventHub::getEvents讀取的事件數(shù)據(jù)結(jié)構(gòu)如下:

    1. struct RawEvent {  
    2.     nsecs_t when;        //事件發(fā)生的時(shí)間  
    3.     int32_t deviceId;    //產(chǎn)生此事件的設(shè)備,比如發(fā)送FINISHED_DEVICE_SCAN,不需要填此項(xiàng)  
    4.     int32_t type;        //事件類型(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN)  
    5.     int32_t scanCode;  
    6.     int32_t keyCode;  
    7.     int32_t value;  
    8.     uint32_t flags;  
    9. };  

    讀取事件時(shí)的調(diào)用流程為:

    Thread::_threadLoop->

         InputReaderThread::threadLoop->

              InputReader::loopOnce->

                   EventHub::getEvents->

    4.1 打開設(shè)備

          在EventHub::getEvents中,當(dāng)mNeedToScanDevices為true時(shí)<當(dāng)創(chuàng)建EventHub對(duì)象時(shí),它就為true>,它將從/dev/input目錄下查找所有設(shè)備,并進(jìn)行打開,獲取其相關(guān)屬性,最后加入mDevices列表中。

    EventHub::scanDevicesLocked->

         EventHub::scanDirLocked("/dev/input")->

             EventHub::openDeviceLocked

    4.1.1 打開事件輸入設(shè)備

         打開事件輸入設(shè)備,在用戶態(tài)調(diào)用open,則在kernel態(tài)中調(diào)用evdev_open函數(shù),evdev_open處理流程如下:

         1)首先從參數(shù)inode中獲取在evdev_table中的索引,從而獲取對(duì)應(yīng)的evdev對(duì)象

         2)創(chuàng)建evdev_client對(duì)象,創(chuàng)建此對(duì)象時(shí)同時(shí)為其buffer成員分配對(duì)應(yīng)的內(nèi)存

         3)把新創(chuàng)建evdev_client對(duì)象添加到client_list鏈表中

         4)把client保存在file的private_data中

         5)調(diào)用evdev_open_device->input_open_device->input_dev.open函數(shù)打開設(shè)備。

     

    4.2 讀取輸入事件

          要說EventHub::getEvents如何獲取輸入事件,不得不先說說它的幾個(gè)相關(guān)的成員變量:

         1)mPendingEventCount:調(diào)用epoll_wait時(shí)的返回值,當(dāng)然如果沒有事件,則其值為0;

         2)mPendingEventIndex:當(dāng)前需要處理的事件索引

         3)mEpollFd:epoll實(shí)例,在EventHub::EventHub中初始化此例,所有輸入事件通過epoll_wait來獲取,每一個(gè)事件的數(shù)據(jù)結(jié)構(gòu)為:struct epoll_event,為了搞明白如何讀取輸入事件的原理,不得不對(duì)epoll相關(guān)的東東搞個(gè)清清楚楚,明明白白,見epoll kernel實(shí)現(xiàn)原理注:epoll_event只表明某個(gè)設(shè)備上有事件,并不包含事件內(nèi)容,具體事件內(nèi)容需要通過read來讀取

       struct epoll_event定義如下:

    1. typedef union epoll_data   
    2. {  
    3.     void *ptr;  
    4.     int fd;  
    5.     unsigned int u32;  
    6.     unsigned long long u64;  
    7. } epoll_data_t;  
    8.   
    9. struct epoll_event   
    10. {  
    11.     unsigned int events;  
    12.     epoll_data_t data;  
    13. };  

         每個(gè)設(shè)備被創(chuàng)建(在函數(shù)EventHub::openDeviceLocked中)時(shí),都會(huì)向epoll注冊(cè),代碼如下:

    1. // Register with epoll.  
    2. struct epoll_event eventItem;  
    3. memset(&eventItem, 0, sizeof(eventItem));  
    4. eventItem.events = EPOLLIN;  
    5. eventItem.data.u32 = deviceId;  
    6. if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {  
    7.     LOGE("Could not add device fd to epoll instance.  errno=%d", errno);  
    8.     delete device;  
    9.     return -1;  
    10. }  

    4.2.1 查看設(shè)備上是否有事件


            在調(diào)用epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之后,讀到的epoll_event事件保存在mPendingEventItems,總共的事件數(shù)保存在mPendingEventCount,當(dāng)然,在調(diào)用epoll_event之前,mPendingEventIndex被清0,直正的事件處理在下面的代碼中。

    1.         // Grab the next input event.  
    2.         bool deviceChanged = false;  
    3.         while (mPendingEventIndex < mPendingEventCount) {  
    4.             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];  
    5.             if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {  
    6.                 if (eventItem.events & EPOLLIN) {  
    7.                     mPendingINotify = true;  
    8.                 } else {  
    9.                     LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);  
    10.                 }  
    11.                 continue;  
    12.             }  
    13.   
    14.             if (eventItem.data.u32 == EPOLL_ID_WAKE) {  
    15.                 if (eventItem.events & EPOLLIN) {  
    16.                     LOGV("awoken after wake()");  
    17.                     awoken = true;  
    18.                     char buffer[16];  
    19.                     ssize_t nRead;  
    20.                     do {  
    21.                         nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));  
    22.                     } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));  
    23.                 } else {  
    24.                     LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",  
    25.                             eventItem.events);  
    26.                 }  
    27.                 continue;  
    28.             }  
    29.   
    30.             ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);  
    31.             if (deviceIndex < 0) {  
    32.                 LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",  
    33.                         eventItem.events, eventItem.data.u32);  
    34.                 continue;  
    35.             }  
    36.   
    37.             Device* device = mDevices.valueAt(deviceIndex);  
    38.             if (eventItem.events & EPOLLIN) {  
    39.                 int32_t readSize = read(device->fd, readBuffer,  
    40.                         sizeof(struct input_event) * capacity);  
    41.                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {  
    42.                     // Device was removed before INotify noticed.  
    43.                     LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",  
    44.                          device->fd, readSize, bufferSize, capacity, errno);  
    45.                     deviceChanged = true;  
    46.                     closeDeviceLocked(device);  
    47.                 } else if (readSize < 0) {  
    48.                     if (errno != EAGAIN && errno != EINTR) {  
    49.                         LOGW("could not get event (errno=%d)", errno);  
    50.                     }  
    51.                 } else if ((readSize % sizeof(struct input_event)) != 0) {  
    52.                     LOGE("could not get event (wrong size: %d)", readSize);  
    53.                 } else {  
    54.                     int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;  
    55.   
    56.                     size_t count = size_t(readSize) / sizeof(struct input_event);  
    57.                     for (size_t i = 0; i < count; i++) {  
    58.                         const struct input_event& iev = readBuffer[i];  
    59.                         LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",  
    60.                                 device->path.string(),  
    61.                                 (int) iev.time.tv_sec, (int) iev.time.tv_usec,  
    62.                                 iev.type, iev.code, iev.value);  
    63.   
    64. #ifdef HAVE_POSIX_CLOCKS  
    65.                         // Use the time specified in the event instead of the current time  
    66.                         // so that downstream code can get more accurate estimates of  
    67.                         // event dispatch latency from the time the event is enqueued onto  
    68.                         // the evdev client buffer.  
    69.                         //  
    70.                         // The event's timestamp fortuitously uses the same monotonic clock  
    71.                         // time base as the rest of Android.  The kernel event device driver  
    72.                         // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().  
    73.                         // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere  
    74.                         // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a  
    75.                         // system call that also queries ktime_get_ts().  
    76.                         event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL  
    77.                                 + nsecs_t(iev.time.tv_usec) * 1000LL;  
    78.                         LOGV("event time %lld, now %lld", event->when, now);  
    79. #else  
    80.                         event->when = now;  
    81. #endif  
    82.                         event->deviceId = deviceId;  
    83.                         event->type = iev.type;  
    84.                         event->scanCode = iev.code;  
    85.                         event->value = iev.value;  
    86.                         event->keyCode = AKEYCODE_UNKNOWN;  
    87.                         event->flags = 0;  
    88.                         if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {  
    89.                             status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,  
    90.                                         &event->keyCode, &event->flags);  
    91.                             LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",  
    92.                                     iev.code, event->keyCode, event->flags, err);  
    93.                         }  
    94.                         event += 1;  
    95.                     }  
    96.                     capacity -= count;  
    97.                     if (capacity == 0) {  
    98.                         // The result buffer is full.  Reset the pending event index  
    99.                         // so we will try to read the device again on the next iteration.  
    100.                         mPendingEventIndex -= 1;  
    101.                         break;  
    102.                     }  
    103.                 }  
    104.             } else {  
    105.                 LOGW("Received unexpected epoll event 0x%08x for device %s.",  
    106.                         eventItem.events, device->identifier.name.string());  
    107.             }  
    108.         }  

     

    4.2.2 讀取設(shè)備上真正的事件

    epoll_wait只是告訴我們Device已經(jīng)有事件了,讓我們?nèi)プx,真正讀取設(shè)備輸入事件的代碼如上,其流程如下:
    1)根據(jù)eventItem.data.u32獲取設(shè)備索引,從而獲取對(duì)應(yīng)的Device

    2)從device->fd中讀取input_event事件。read(device->fd, readBuffer, sizeof(struct input_event) * capacity);這些input_event是由各個(gè)注冊(cè)的input_device報(bào)告給input子系統(tǒng)的。具體讀入流程參見Input Core和evdev基本知識(shí) - Kernel3.0.8

    至此,事件已經(jīng)讀取到用戶態(tài),哪我們就看看EventHub怎么處理這些事件了。 

    4.3 處理輸入事件

          在4.2中,首先通過epoll_wait查看哪些設(shè)備有事件,然后通過read從有事件的設(shè)備中讀取事件,現(xiàn)在事件已經(jīng)讀取到用戶態(tài),且數(shù)據(jù)結(jié)構(gòu)為input_event,保存在EventHub::getEvents的readBuffer中。下面就看看這些事件下一步的東家是誰?

          1)首先把input_event的信息填入RawEvent中,其相關(guān)代碼如下:

    1. #ifdef HAVE_POSIX_CLOCKS  
    2.                         // Use the time specified in the event instead of the current time  
    3.                         // so that downstream code can get more accurate estimates of  
    4.                         // event dispatch latency from the time the event is enqueued onto  
    5.                         // the evdev client buffer.  
    6.                         //  
    7.                         // The event's timestamp fortuitously uses the same monotonic clock  
    8.                         // time base as the rest of Android.  The kernel event device driver  
    9.                         // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().  
    10.                         // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere  
    11.                         // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a  
    12.                         // system call that also queries ktime_get_ts().  
    13.                         event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL  
    14.                                 + nsecs_t(iev.time.tv_usec) * 1000LL;  
    15.                         LOGV("event time %lld, now %lld", event->when, now);  
    16. #else  
    17.                         event->when = now;  
    18. #endif  
    19.                         event->deviceId = deviceId;  
    20.                         event->type = iev.type;  
    21.                         event->scanCode = iev.code;  
    22.                         event->value = iev.value;  
    23.                         event->keyCode = AKEYCODE_UNKNOWN;  
    24.                         event->flags = 0;  
    25.                         if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {  
    26.                             status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,  
    27.                                         &event->keyCode, &event->flags);  
    28.                             LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",  
    29.                                     iev.code, event->keyCode, event->flags, err);  
    30.                         }  


         2)如果是input_event的類型為EV_KEY,則需要調(diào)用device->keyMap.keyLayoutMap->mapKey函數(shù)把iput_event.code映射為RawEvent.keyCode。相關(guān)數(shù)據(jù)結(jié)構(gòu)關(guān)系如下圖所示:

           至此,EventHub::getEvents讀取事件的任務(wù)已經(jīng)完成,下面看看這些RawEvent的命運(yùn)如何呢?

     4.3.1 InputReader::loopOnce如何處理RawEvent?

        為此,先溫習(xí)一下讀取事件時(shí)的調(diào)用流程為:

    Thread::_threadLoop->

         InputReaderThread::threadLoop->

              InputReader::loopOnce->

                   EventHub::getEvents->

         在InputReader::loopOnce中,當(dāng)調(diào)用EventHub->getEvents獲取到RawEvent之后,調(diào)用InputReader::processEventsLocked來處理這些事件,然后調(diào)用mQueuedListener->flush()把這些隊(duì)列中的事件發(fā)送到Listener。

    4.3.1.1 InputReader::processEventsLocked

           在InputReader::processEventsLocked主要分兩步處理:

           1)處理來自于事件驅(qū)動(dòng)設(shè)備的事件(processEventsForDeviceLocked)

           2)處理設(shè)備增加、刪除和修改事件

           按照程序執(zhí)行流程,應(yīng)該是先有設(shè)備,然后才會(huì)有設(shè)備事件,所以先分析設(shè)備增加。 其代碼如下:

     

    1. <span style="font-size:10px;">void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count)   
    2. {  
    3.     for (const RawEvent* rawEvent = rawEvents; count;) {  
    4.         int32_t type = rawEvent->type;  
    5.         size_t batchSize = 1;  
    6.           
    7.        //處理來自于事件驅(qū)動(dòng)設(shè)備的事件  
    8.         if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {  
    9.           
    10.             int32_t deviceId = rawEvent->deviceId;  
    11.             while (batchSize < count) {  
    12.                 if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT  
    13.                         || rawEvent[batchSize].deviceId != deviceId) {  
    14.                     break;  
    15.                 }  
    16.                 batchSize += 1;  
    17.             }  
    18.           //處理來自于同一個(gè)事件驅(qū)動(dòng)設(shè)備的1個(gè)或多個(gè)事件  
    19.             processEventsForDeviceLocked(deviceId, rawEvent, batchSize);  
    20.         }   
    21.         else   
    22.         {  
    23.             //處理增加或刪除事件驅(qū)動(dòng)設(shè)備的事件,在EventHub::getEvents中產(chǎn)生,  
    24.             //不是由事件驅(qū)動(dòng)設(shè)備產(chǎn)生的。  
    25.             switch (rawEvent->type) {  
    26.             case EventHubInterface::DEVICE_ADDED:  
    27.                 addDeviceLocked(rawEvent->when, rawEvent->deviceId);  
    28.                 break;  
    29.             case EventHubInterface::DEVICE_REMOVED:  
    30.                 removeDeviceLocked(rawEvent->when, rawEvent->deviceId);  
    31.                 break;  
    32.             case EventHubInterface::FINISHED_DEVICE_SCAN:  
    33.                 handleConfigurationChangedLocked(rawEvent->when);  
    34.                 break;  
    35.             default:  
    36.                 LOG_ASSERT(false); // can't happen  
    37.                 break;  
    38.             }  
    39.         }  
    40.         count -= batchSize;  
    41.         rawEvent += batchSize;  
    42.     }  
    43. }</span>  

     

    4.3.1.1.1 設(shè)備增加事件處理 addDeviceLocked

          它處理其中的EventHubInterface::DEVICE_ADDED, EventHubInterface:: DEVICE_REMOVED和EventHubInterface::FINISHED_DEVICE_SCAN事件,即與Device相關(guān)的事件,這些事件是在EventHub::getEvents中產(chǎn)生的,并不是Kernel態(tài)的事件輸入設(shè)備產(chǎn)生的。

         下面分析它如何處理EventHubInterface::DEVICE_ADDED事件。查看其它代碼,它是調(diào)用InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId)來處理此事件。

          在InputReader::addDeviceLocked中的調(diào)用流程:

          1)先根據(jù)mContext, deviceId, name, classes創(chuàng)建一個(gè)InputDevice對(duì)象,它用于表示單個(gè)輸入設(shè)備的狀態(tài)。其中的classes為對(duì)應(yīng)Device的classes成員,它用于表示設(shè)備類型,其定義如下:

    1. /*  
    2.  * Input device classes.  
    3.  */  
    4. enum {  
    5.     /* The input device is a keyboard or has buttons. */  
    6.     INPUT_DEVICE_CLASS_KEYBOARD      = 0x00000001,  
    7.   
    8.     /* The input device is an alpha-numeric keyboard (not just a dial pad). */  
    9.     INPUT_DEVICE_CLASS_ALPHAKEY      = 0x00000002,  
    10.   
    11.     /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */  
    12.     INPUT_DEVICE_CLASS_TOUCH         = 0x00000004,  
    13.   
    14.     /* The input device is a cursor device such as a trackball or mouse. */  
    15.     INPUT_DEVICE_CLASS_CURSOR        = 0x00000008,  
    16.   
    17.     /* The input device is a multi-touch touchscreen. */  
    18.     INPUT_DEVICE_CLASS_TOUCH_MT      = 0x00000010,  
    19.   
    20.     /* The input device is a directional pad (implies keyboard, has DPAD keys). */  
    21.     INPUT_DEVICE_CLASS_DPAD          = 0x00000020,  
    22.   
    23.     /* The input device is a gamepad (implies keyboard, has BUTTON keys). */  
    24.     INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,  
    25.   
    26.     /* The input device has switches. */  
    27.     INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,  
    28.   
    29.     /* The input device is a joystick (implies gamepad, has joystick absolute axes). */  
    30.     INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,  
    31.   
    32.     /* The input device is external (not built-in). */  
    33.     INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,  
    34. }  

          創(chuàng)建InputDevice對(duì)象之后, 對(duì)于多點(diǎn)觸摸設(shè)備(class為INPUT_DEVICE_CLASS_TOUCH_MT),創(chuàng)建MultiTouchInputMapper對(duì)象并增加到InputDevice的mMappers向量列表中。

          對(duì)于單點(diǎn)觸摸設(shè)備(class為INPUT_DEVICE_CLASS_TOUCH),創(chuàng)建SingleTouchInputMapper對(duì)象并增加到InputDevice的mMappers向量列表中。相關(guān)代碼如下:

    1. InputDevice* InputReader::createDeviceLocked(int32_t deviceId,  
    2.         const String8& name, uint32_t classes) {  
    3.     InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);  
    4.   
    5.     ....  
    6.   
    7.     if (keyboardSource != 0) {  
    8.         device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));  
    9.     }  
    10.   
    11.     // Cursor-like devices.  
    12.     if (classes & INPUT_DEVICE_CLASS_CURSOR) {  
    13.         device->addMapper(new CursorInputMapper(device));  
    14.     }  
    15.   
    16.     // Touchscreens and touchpad devices.  
    17.     if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {  
    18.         device->addMapper(new MultiTouchInputMapper(device));  
    19.     } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {  
    20.         device->addMapper(new SingleTouchInputMapper(device));  
    21.     }  
    22.   
    23.     // Joystick-like devices.  
    24.     if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {  
    25.         device->addMapper(new JoystickInputMapper(device));  
    26.     }  
    27.   
    28.     return device;  
    29. }  


         總之,它調(diào)用createDeviceLocked創(chuàng)建一個(gè)InputDevice設(shè)備,并根據(jù)class類別創(chuàng)建對(duì)應(yīng)的事件轉(zhuǎn)換器(InputMapper),然后把這些新那建的InputMapper增加到InputDevice::mMappers中。InputMapper關(guān)系如下圖所示:


            

       2)調(diào)用InputDevice::configure配置此InputDevice

       3)調(diào)用InputDevice::reset重置此InputDevice

       4)把新建的InputDevice增加到InputReader::mDevices中。

       InputReader::processEventsLocked設(shè)備增加、刪除處理總結(jié):

         它負(fù)責(zé)處理Device 增加、刪除事件。增加事件的流程為:為一個(gè)新增的Device創(chuàng)建一個(gè)InputDevice,并增加到InputReader::mDevices中;根據(jù)新增加設(shè)備的class類別,創(chuàng)建對(duì)應(yīng)的消息轉(zhuǎn)換器(InputMapper),然后此消息轉(zhuǎn)換器加入InputDevice::mMappers中。消息轉(zhuǎn)換器負(fù)責(zé)把讀取的RawEvent轉(zhuǎn)換成特定的事件,以供應(yīng)用程序使用。

         EventHub與InputReader各自管理功能:

         1)EventHub管理一堆Device,每一個(gè)Device與Kernel中一個(gè)事件輸入設(shè)備對(duì)應(yīng)

         2)InputReader管理一堆InputDevice,每一個(gè)InputDevice與EventHub中的Device對(duì)應(yīng)

         3)InputDevice管理一些與之相關(guān)的InputMapper,每一個(gè)InputMapper與一個(gè)特定的應(yīng)用事件相對(duì)應(yīng),如:SingleTouchInputMapper。

              

    4.3.1.1.2 事件驅(qū)動(dòng)設(shè)備事件處理processEventsForDeviceLocked

       下面的分析處理以單點(diǎn)觸摸為例,對(duì)于單點(diǎn)觸摸Touch Down時(shí),它將報(bào)告以下事件:

        代碼:

        input_report_abs(myInputDev, ABS_X, event->x);
        input_report_abs(myInputDev, ABS_Y, event->y);

        產(chǎn)生的事件:*type, code, value
                              EV_ABS,ABS_X,event->x
                              EV_ABS,ABS_Y,event->y     

        代碼: 

        input_report_key(myInputDev, BTN_TOUCH,  1);
        產(chǎn)生的事件:*type, code, value
                              EV_KEY, BTN_TOUCH, 1

         代碼:

          input_sync(myInputDev);
            它調(diào)用input_event(dev, EV_SYN, SYN_REPORT, 0);
         產(chǎn)生的事件:*type, code, value
                               EV_SYN, SYN_REPORT, 0

          

         它負(fù)責(zé)處理來自于同一個(gè)設(shè)備且在mEventBuffer中連續(xù)的多個(gè)事件,其函數(shù)原型如下:

    1. void InputReader::processEventsForDeviceLocked(int32_t deviceId,  
    2.         const RawEvent* rawEvents, size_t count) {  
    3.     ssize_t deviceIndex = mDevices.indexOfKey(deviceId);  
    4.     if (deviceIndex < 0) {  
    5.         LOGW("Discarding event for unknown deviceId %d.", deviceId);  
    6.         return;  
    7.     }  
    8.   
    9.     InputDevice* device = mDevices.valueAt(deviceIndex);  
    10.     if (device->isIgnored()) {  
    11.         //LOGD("Discarding event for ignored deviceId %d.", deviceId);  
    12.         return;  
    13.     }  
    14.   
    15.     device->process(rawEvents, count);  
    16. }  

    它其實(shí)很簡(jiǎn)單,根據(jù)輸入的deviceId找到對(duì)應(yīng)的InputDevice,然后調(diào)用InputDevice::process以對(duì)設(shè)備輸入事件進(jìn)行處理。InputDevice::process主要源碼如下:

     

    1. void InputDevice::process(const RawEvent* rawEvents, size_t count) {  
    2.     // Process all of the events in order for each mapper.  
    3.     // We cannot simply ask each mapper to process them in bulk because mappers may  
    4.     // have side-effects that must be interleaved.  For example, joystick movement events and  
    5.     // gamepad button presses are handled by different mappers but they should be dispatched  
    6.     // in the order received.  
    7.   
    8.     size_t numMappers = mMappers.size();  
    9.     for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++)   
    10.     {  
    11.         for (size_t i = 0; i < numMappers; i++) {  
    12.             InputMapper* mapper = mMappers[i];  
    13.             mapper->process(rawEvent);  
    14.         }  
    15.     }  
    16. }  

           從上面的代碼中可以看出,在InputDevice::process中,對(duì)于傳入的每一個(gè)RawEvent,依次調(diào)用InputDevice中的每一個(gè)InputMapper來進(jìn)行處理。前面提到過,InputDevice包含一組處理對(duì)應(yīng)設(shè)備事件InputMapper,現(xiàn)在這些InputMapper開始干活了。
          下面以處理一個(gè)單點(diǎn)觸摸事件設(shè)備的事件為例,進(jìn)行分析,其它的處理流程類似。對(duì)于mapper->process需要查看InputReader::createDeviceLocked中創(chuàng)建的具體的InputMapper的process函數(shù)。下面就看看SingleTouchInputMapper的process是如何處理的,其代碼如下:

    1. void SingleTouchInputMapper::process(const RawEvent* rawEvent) {  
    2.     TouchInputMapper::process(rawEvent);  
    3.   
    4.     mSingleTouchMotionAccumulator.process(rawEvent);  
    5. }  

    1)TouchInputMapper::process

           由此可見,它將首先調(diào)用TouchInputMaaper::process處理此事件,其處理代碼如下:

    1. void TouchInputMapper::process(const RawEvent* rawEvent) {  
    2.     mCursorButtonAccumulator.process(rawEvent);  
    3.     mCursorScrollAccumulator.process(rawEvent);  
    4.     mTouchButtonAccumulator.process(rawEvent);  
    5.   
    6.     if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {  
    7.         sync(rawEvent->when);  
    8.     }  
    9. }  


    1.1) mCursorButtonAccumulator.process(rawEvent)

         記錄mouse或touch pad按鍵狀態(tài),記錄rawEvent->type為EV_KEY,且rawEvent->scanCode為BTN_LEFT、BTN_RIGHT、BTN_MIDDLE、BTN_BACK、BTN_SIDE、BTN_FORWARD、BTN_EXTRA、BTN_TASK的事件。

    1.2) mCursorScrollAccumulator.process(rawEvent)

         記錄cursor scrolling motions,記錄rawEvent->type為EV_REL,且rawEvent->scanCode為REL_WHEEL、REL_HWHEEL的事件。

    1.3) mTouchButtonAccumulator.process(rawEvent)

         記錄touch, stylus and tool buttons狀態(tài),記錄rawEvent->type為EV_KEY,且rawEvent->scanCode為BTN_TOUCH、BTN_STYLUS、BTN_STYLUS2、BTN_TOOL_FINGER、BTN_TOOL_PEN、BTN_TOOL_RUBBER、BTN_TOOL_BRUSH、BTN_TOOL_PENCIL、BTN_TOOL_AIRBRUSH、BTN_TOOL_MOUSE、BTN_TOOL_LENS、BTN_TOOL_DOUBLETAP、BTN_TOOL_TRIPLETAP、BTN_TOOL_QUADTAP的事件。

         看到了吧,我們的BTN_TOUCH在這兒被處理了,且其value被保存在mBtnTouch成員變量中。

    1.4) sync(rawEvent->when)

          處理EV_SYN:SYN_REPORT,我們的EV_SYN就在這兒被處理了,當(dāng)然它是Touch Down時(shí),所發(fā)事件的最后一個(gè)事件。這兒才是處理的重點(diǎn)。

          TouchInputMapper::sync將調(diào)用SingleTouchInputMapper::syncTouch函數(shù)。

          a)SingleTouchInputMapper::syncTouch

          把mCurrentRawPointerData中的ABS_X和ABS_Y的值保存在TouchInputMapper::mCurrentRawPointerData->pointers中。

              單點(diǎn)觸摸的syncTouch一次處理一個(gè)RawEvent,在pointers中只有一個(gè)值;而多點(diǎn)觸摸的syncTouch一次處理多個(gè)RawEvent,在pointers中有多個(gè)值,最多16個(gè)。

          b)TouchInputMapper::cookPointerData

          根據(jù)TouchInputMapper::mCurrentRawPointerData->pointers中的數(shù)據(jù),通過計(jì)算,最后生成TouchInputMapper::mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.pointerProperties和mCurrentCookedPointerData.idToIndex的數(shù)據(jù)。把Raw進(jìn)行cook,之后生成了cooked數(shù)據(jù)。

          c)TouchInputMapper::dispatchHoverExit

     

          d)TouchInputMapper::dispatchTouches

          d.a)它調(diào)用dispatchMotion

          d.b)在dispatchMotion中,根據(jù)cooked數(shù)據(jù)創(chuàng)建NotifyMotionArg對(duì)象,它描述了一個(gè)移動(dòng)事件

          d.c)調(diào)用TouchInputMapper::getListener()->notifyMotion(&args)

                  TouchInputMapper::getListener()調(diào)用mContext->getListener(),此mContext為InputReader::mContext,所以其getListener()返回的則為InputReader::mQueuedListener,則最后調(diào)用QueuedInputListener::notifyMotion

           補(bǔ)充1) InputReader::mContext在構(gòu)造時(shí)用自己的指針初始化了mContext,從而mContext::mReader則為此InputReader實(shí)例。
           補(bǔ)充2) 在InputReader::createDeviceLocked中創(chuàng)建InputDevice時(shí),把自己的mContext作為參數(shù)傳入,從而把它保存在InputDevice::mContext中;在創(chuàng)建InputMapper時(shí),以InputDevice作為參數(shù),且InputMapper把它保存在mDevice中,然后從把InputDevice中的mContext也保存在InputMapper的mContext中。

          d.d)把傳遞過來的NotifyMotionArg參數(shù)復(fù)制一份,然后加入QueuedInputListener::mArgsQueue例表中。

     

          e)TouchInputMapper::dispatchHoverEnterAndMove

              

             

     

    2)mSingleTouchMotionAccumulator.process 


         記錄ABS相關(guān)的值,記錄rawEvent->type為EV_ABS,且rawEvent->scanCode為ABS_X、ABS_Y、ABS_PRESSURE、ABS_TOOL_WIDTH、ABS_DISTANCE、ABS_TILT_X、ABS_TILT_Y的事件。我們發(fā)的ABS_X和ABS_Y在這兒被處理了。

     

         事件處理相關(guān)數(shù)據(jù)結(jié)構(gòu)如下圖所示:     

     

     

    4.3.1.2 InputReader::mQueuedListener->flush()

          先溫習(xí)一下,至此的消息結(jié)構(gòu)變化流程:

          

          processEventsLocked已經(jīng)把來自于事件設(shè)備的事件煮熟之后放入到各種NotifyArgs(如NotifyMotionArgs)之中,然后把這些各種NotifyArgs加入InputReader::mQueuedListener::mArgsQueue鏈表中。本Flush函數(shù)就是要把mArgsQueue中的所有NotifyArgs進(jìn)行處理。為描述方便,先看看其代碼:

     

    1. void QueuedInputListener::flush() {  
    2.     size_t count = mArgsQueue.size();  
    3.     for (size_t i = 0; i < count; i++) {  
    4.         NotifyArgs* args = mArgsQueue[i];  
    5.         args->notify(mInnerListener);  
    6.         delete args;  
    7.     }  
    8.     mArgsQueue.clear();  
    9. }  


           看到了吧,確實(shí)很簡(jiǎn)單,調(diào)用鏈表中每個(gè)NotifyArgs的notify函數(shù),且有一個(gè)有意思的參數(shù) mInnerListener,這個(gè)參數(shù)在前面多次提到過,它是在創(chuàng)建mQueuedListener時(shí)提供的,它其實(shí)就是InputManager中的mDispatcher,前面一直在InputReader中打轉(zhuǎn)轉(zhuǎn),現(xiàn)在終于看到InputDispatcher登場(chǎng)了,說明事件很快就可以謝幕了。

           再向下看一下吧,這么多類NotifyArgs,為描述方便,下面以NotifyMotionArgs為例,其代碼為: 

      

    1. void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {  
    2.     listener->notifyMotion(this);  
    3. }  

          下面就看看InputDispatcher(mDispatcher)的notifyMotion函數(shù)做了些什么。這個(gè)InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不簡(jiǎn)單了。

           在InputDispatcher::notifyMotion中,
           1)根據(jù)NotifyMotionArgs提供的信息,構(gòu)造一個(gè)MotionEvent,再調(diào)用mPolicy->filterInputEvent看是否需要丟棄此事件,如果需要丟棄則馬上返加。其中mPolicy為NativeInputManager實(shí)例,在構(gòu)造InputDispatcher時(shí)提供的參數(shù)。

           2)對(duì)于AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,則直接根據(jù)NotifyMotionArgs提供的信息,構(gòu)造一個(gè)MotionEntry。

           3)調(diào)用InputDispatcher::enqueueInboundEventLocked把新構(gòu)造的MotionEntry添加到InputDispatcher::mInboundQueue中,并返回是否需要喚醒mLooper<向pipe中寫入數(shù)據(jù)>的標(biāo)識(shí)。

          以上操作都是在InputReader線程中完成的,現(xiàn)在應(yīng)該InputDispatcher線程開始工作了。

    4. 4 分發(fā)輸入事件

    InputDispatcherThread主循環(huán)如下:

    Thread::_threadLoop->

       InputDispatcherThread::threadLoop->

          mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->

              dispatchOnceInnerLocked then

              mLooper->pollOnce

    下面先看看簡(jiǎn)單的mLooper->pollOnce

     4.4.1 mLooper->pollOnce 

          其功能為等待超時(shí)或被pipe喚醒(InputReader線程調(diào)用InputDispatcher::notifyMotion時(shí), InputDispatcher::notifyMotion根據(jù)情況調(diào)用mLooper->wake)。

          其調(diào)用流程如下:

          mLooper->pollOnce(int timeoutMillis)->

             Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->

    4.4.2 dispatchOnceInnerLocked         

          1)從mInboundQueue從中依次取出EventEntry<MotionEntry的基類>

          2)調(diào)用InputDispatcher::dispatchMotionLocked處理此MotionEntry

          3)調(diào)用InputDispatcher::dispatchEventToCurrentInputTargetsLocked

                對(duì)于InputDispatcher::mCurrentInputTargets中的每一個(gè)InputTarget,并獲取對(duì)應(yīng)的Connection,調(diào)用InputDispatcher::prepareDispatchCycleLocked,

    其相關(guān)代碼如下:

     

    1.   <span style="font-size:10px;">  for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {  
    2.         const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);  
    3.   
    4.         ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);  
    5.         if (connectionIndex >= 0) {  
    6.             sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);  
    7.             prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,  
    8.                     resumeWithAppendedMotionSample);  
    9.         } else {  
    10. #if DEBUG_FOCUS  
    11.             LOGD("Dropping event delivery to target with channel '%s' because it "  
    12.                     "is no longer registered with the input dispatcher.",  
    13.                     inputTarget.inputChannel->getName().string());  
    14. #endif  
    15.         }  
    16.     }</span>  

          4)InputDispatcher::prepareDispatchCycleLocked

               4.1)調(diào)用enqueueDispatchEntryLocked創(chuàng)建DispatchEntry對(duì)象,并把它增加到Connection::outboundQueue隊(duì)列中。

               4.2)調(diào)用activateConnectionLocked把當(dāng)前Connection增加到InputDispatcher::mActiveConnections鏈表中

               4.3)調(diào)用InputDispatcher::startDispatchCycleLocked,接著它調(diào)用Connection::inputPublisher.publishMotionEvent來發(fā)布事件到ashmem buffer中,調(diào)用Connection::inputPublisher.sendDispatchSignal發(fā)送一個(gè)dispatch信號(hào)到InputConsumer通知它有一個(gè)新的消息到了,快來消費(fèi)吧!  關(guān)于消費(fèi)者如何注冊(cè)和如何消息的流程在下一個(gè)專題中再寫。本文到此結(jié)束!!!   

         

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     


     

     

     

     

     

     

    posted on 2012-01-05 16:33 MEYE 閱讀(11283) 評(píng)論(2)  編輯  收藏 所屬分類: Android3D

    FeedBack:
    # re: Android 4.0 事件輸入(Event Input)系統(tǒng)
    2012-10-30 11:56 |
    你畫的類圖真心有問題  回復(fù)  更多評(píng)論
      
    # re: Android 4.0 事件輸入(Event Input)系統(tǒng)[未登錄]
    2014-09-04 14:46 | george
    挺好的,受益匪淺!  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲AV第一页国产精品| 4虎1515hh永久免费| 亚洲女同成人AⅤ人片在线观看| 亚洲av无码兔费综合| 好吊妞788免费视频播放| 中文字幕亚洲男人的天堂网络 | 亚洲人成电影在线天堂| 久久国产精品成人免费| 久久精品国产亚洲AV麻豆王友容| 成人A片产无码免费视频在线观看| 亚洲VA中文字幕不卡无码| 无码人妻精品中文字幕免费 | 亚洲av午夜成人片精品电影| 黄色一级视频免费| 亚洲高清视频一视频二视频三| 无遮挡国产高潮视频免费观看| 亚洲精品一级无码鲁丝片| 国产精品永久免费| 国产亚洲福利精品一区| 免费无码一区二区三区| 亚洲精品中文字幕无乱码| 免费在线看v网址| 国产成人亚洲综合网站不卡| 国产精品另类激情久久久免费 | 亚洲精品国产免费| 国产精品高清视亚洲精品| 日韩在线视频免费看| 日本免费精品一区二区三区| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 亚洲国产成人一区二区三区 | 国产免费69成人精品视频| 夜夜爽妓女8888视频免费观看| 国产AV无码专区亚洲AV手机麻豆| 野花香在线视频免费观看大全| 久久亚洲日韩看片无码| 一二三四视频在线观看中文版免费| 亚洲爆乳少妇无码激情| 亚洲精品A在线观看| 国产麻豆成人传媒免费观看 | 男女一进一出抽搐免费视频| 亚洲国产精品自在线一区二区|