日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Android 输入系统介绍

發布時間:2023/11/25 windows 26 coder
生活随笔 收集整理的這篇文章主要介紹了 Android 输入系统介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄
  • 一、目的
  • 二、環境
  • 三、相關概念
    • 3.1 輸入設備
    • 3.2 UEVENT機制
    • 3.3 JNI
    • 3.4 EPOLL機制
    • 3.5 INotify
  • 四、詳細設計
    • 4.1 結構圖
    • 4.2 代碼結構
    • 4.3 InputManagerService模塊
      • 4.3.1 IMS服務入口
      • 4.3.2 IMS初始化
      • 4.3.3 IMS啟動
      • 4.3.4 IMS消息監聽
    • 4.4 NativeInputManager模塊
      • 4.4.1 nativeInit初始化
      • 4.4.2 nativeStart啟動
    • 4.5 Inputflinger模塊
      • 4.5.1 啟動事件管理服務
    • 4.6 Inputreader模塊
      • 4.6.1 啟動InputReader線程
      • 4.6.2 EventHub獲取事件隊列
      • 4.6.3 Input事件加工
      • 4.6.4 事件發布
    • 4.7 Inputdispatcher模塊
      • 4.7.1 Input事件上報
      • 4.7.2 啟動InputDispatcher線程
    • 4.8 WindowManagerService模塊
      • 4.8.1 ViewRootImpl階段
      • 4.8.2 WindowManagerService.addWindow()
      • 4.8.3 WindowInputEventReceiver
  • 五、Input設備節點介紹
    • 5.1 常見觸摸事件類型
    • 5.2 getevent
    • 5.3 sendevent
  • 六、參考資料

一、目的

????????最近接觸到了一個問題:耳機插入事件的由來,走讀了下IMS輸入系統服務的源碼。同時,IMS輸入系統服務在Android的開發過程中,也經常出現,有必要了解下相關原理。

  1. 學習下IMS輸入系統的源碼設計,了解該模塊承擔的業務職責,熟悉Android結構
  2. 了解Android屏幕點擊事件、物理按鍵事件的分發規則

二、環境

  1. 版本:Android 11
  2. 平臺:高通 QCM2290

三、相關概念

3.1 輸入設備

????????常見的輸入設備有鼠標、鍵盤、觸摸屏等,用戶通過輸入設備與系統進行交互。

3.2 UEVENT機制

????????"uevent" 是 Linux 系統中的一種事件通知機制,用于向用戶空間發送有關內核和設備狀態變化的通知。這種機制通常用于設備驅動程序、熱插拔事件以及設備狀態變化等場景,以便用戶空間應用程序能夠在這些事件發生時做出相應的響應。

3.3 JNI

????????JNI,全稱Java Native Interface,是Java編程語言的一種編程框架,用于實現Java代碼與其他編程語言(如C、C++)進行交互的接口。JNI允許Java程序調用原生代碼(native code),即由其他編程語言編寫的代碼,并且允許原生代碼調用Java代碼。通過JNI,Java程序可以訪問底層系統功能、使用硬件設備、調用第三方庫等。

3.4 EPOLL機制

????????監聽多個描述符的可讀/可寫狀態。等待返回時攜帶了可讀的描述符

3.5 INotify

????????Linux 內核所提供的一種文件系統變化通知機制。可以監控文件系統的變化,如文件新建、刪除、讀寫等

四、詳細設計

通過屏幕的觸摸事件,來分析IMS系統,相關如下

4.1 結構圖

4.2 代碼結構

層級 模塊 描述 源碼 編譯產物
Framework InputManagerService xxx frameworks/base/services/core/java/ out/target/product/qssi/system/framework/services.jar
Native NativeInputManager xxx frameworks/base/services/core/jni/ out/target/product/qssi/system/lib64/libandroid_servers.so
Native Inputflinger xxx frameworks/native/services/inputflinger/ out/target/product/qssi/system/lib64/libinputflinger.so
Native Inputreader xxx frameworks/native/services/inputflinger/reader out/target/product/qssi/system/lib64/libinputreader.so
Native Inputdispatcher xxx frameworks/native/services/inputflinger/dispatcher/ (靜態庫)out/soong/.intermediates/frameworks/native/services/inputflinger/dispatcher/libinputdispatcher/android_arm64_armv8-a_static/libinputdispatcher.a
Native NativeInputEventReceiver xxx frameworks/base/core/jni/ out/target/product/qssi/system/lib64/libandroid_runtime
Native InputChannel xxx frameworks/native/libs/input/ out/target/product/qssi/system/lib64/libinput.so

4.3 InputManagerService模塊

????????InputManagerService是Android框架層一個非核心服務,主要是提供一個IMS輸入系統啟動的入口,同時對應用層提供業務相關接口。

4.3.1 IMS服務入口

????????Android設備開機后,會啟動system_server進程,InputManagerService服務(以下簡稱IMS)在該進程被喚起。

@frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
    t.traceBegin("StartInputManagerService");
    inputManager = new InputManagerService(context);//新建IMS實例
    t.traceEnd();
    ...
    t.traceBegin("StartInputManager");
    inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());//設置窗體事件監聽
    inputManager.start();//啟動IMS服務
    t.traceEnd();
    ...
}

4.3.2 IMS初始化

????????此處做一些IMS相關的初始化操作,會調用nativeInit方法,獲取一個NativeInputManager對象,類似于一個句柄。

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native long nativeInit(InputManagerService service,
        Context context, MessageQueue messageQueue);
public InputManagerService(Context context) {
    ...
    mStaticAssociations = loadStaticInputPortAssociations();
    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
    Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
            + mUseDevInputEventForAudioJack);
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
    ...
}

4.3.3 IMS啟動

????????InputManagerService通過start方法啟動,會調用nativeStart方法,該方法為Native方法

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native void nativeStart(long ptr);
public void start() {
    Slog.i(TAG, "Starting input manager");
    nativeStart(mPtr);

    // Add ourself to the Watchdog monitors.
    Watchdog.getInstance().addMonitor(this);
    ...
}

4.3.4 IMS消息監聽

????????該方法為Native的回調方法,用于上報IMS事件,如耳機插入事件等。

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
    ...
    if ((switchMask & SW_LID_BIT) != 0) {
        final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
        mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
    }

    if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
        final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
        mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
    }

    if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
        mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
                    switchMask);
    }
    ...
}

4.4 NativeInputManager模塊

????????該模塊為JNI模塊,主要處理Java方法與c++方法映射關系,即IMS服務與InputFlinger模塊的通信橋梁。

4.4.1 nativeInit初始化

(1)新建一個NativeInputManager對象,并將該對象返回給java層

@\frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

(2)創建InputManager管理類,主要用于管理Input事件分發、事件讀取行為

@\frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
    ...
    mInputManager = new InputManager(this, this);
    defaultServiceManager()->addService(String16("inputflinger"),
            mInputManager, false);
}

4.4.2 nativeStart啟動

獲取上一個階段創建NativeInputManager對象,并引用start啟動該模塊

@\frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

4.5 Inputflinger模塊

input事件的管理類,數據傳遞類,也是輸入系統native層核心的模塊。
ps: 根據字典里的定義,flinger是指出軌的人。在SurfaceFlinger的例子中,它把可視數據扔給surface AudioFlinger把音頻數據扔給適當的接收者。它們只是“可愛”的詞… ??

4.5.1 啟動事件管理服務

????????啟動兩個核心的阻塞線程,一個是事件分發線程,一個是事件讀取線程。

@frameworks\native\services\inputflinger\InputManager.cpp
status_t InputManager::start() {
    status_t result = mDispatcher->start();//啟動事件分發服務
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReader->start();//啟動事件讀取服務
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);

        mDispatcher->stop();
        return result;
    }

    return OK;
}

4.6 Inputreader模塊

????????事件讀取服務,讀取驅動上報事件

4.6.1 啟動InputReader線程

(1)創建一個InputThread線程

@frameworks\native\services\inputflinger\reader\InputReader.cpp
status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

(2)InputThread線程的loop循環隊列(線程和loop的關系)

@frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    ...
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//step 1. 通過EventHub抽取事件列表
    { // acquire lock
        ...
        if (count) {
            processEventsLocked(mEventBuffer, count);// step 2. 對事件進行加工處理
        }
        ...
    } // release lock
    ...
    mQueuedListener->flush();//step 3. 事件發布
}

4.6.2 EventHub獲取事件隊列

????????EventHub:事件集線器,它將全部的輸入事件通過一個接口getEvents(),將從多個輸入設備節點中讀取的事件交給InputReader,是輸入系統最底層的一個組件。
(1)EventHub的構造函數
????????它通過INotifyEpoll機制建立起了對設備節點增刪事件以及可讀狀態的監聽。同時,EventHub創建了一個名為wakeFds的匿名管道,因為InputReader在運行getEvents()時會因無事件而導致其線程堵塞在epoll_wait()的調用里,然而有時希望能夠立馬喚醒InputReader線程使其處理一些請求。

@frameworks\native\services\inputflinger\reader\EventHub.cpp
static const char* DEVICE_PATH = "/dev/input";
EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mOpeningDevices(nullptr),
        mClosingDevices(nullptr),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {
    ensureProcessCanBlockSuspend();

    mEpollFd = epoll_create1(EPOLL_CLOEXEC);//創建一個epoll對象,用來監聽設備節點是否有事件
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));

    mINotifyFd = inotify_init();//創建一個inotify對象,用來監聽設備節點的增刪事件
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    ...
    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = mINotifyFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);//將mINotifyFd注冊進epoll對象中
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);

    int wakeFds[2];
    result = pipe(wakeFds);//創建一個匿名管道,用于喚醒EventHub,避免無事件引起阻塞
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
    ...
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);//將管道讀取端加入epoll對象中
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                        errno);
}

mEpollFd監聽如下幾個事件:設備節點的增加、刪除、修改;匿名管道,避免無事件阻塞

(2)RawEvent結構體
????????mEventBuffer用于描述原始輸入事件,其類型為RawEvent,相關結構體如下:

@frameworks\native\services\inputflinger\reader\include\EventHub.h
/*
 * A raw event as retrieved from the EventHub.
 */
struct RawEvent {
    nsecs_t when;//事件時間戳
    int32_t deviceId;//產生事件的設備ID
    int32_t type;//事件類型
    int32_t code;//事件編碼
    int32_t value;//事件值
};

(3)EventHub->getEvents事件,
????????getEvents()是事件處理的核心方法,其通過EPOLL機制和INOTIFY,從多個設備節點讀取事件。

@frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ...
    for (;;) {
        ...
        if (mNeedToScanDevices) {//Step 1.掃描設備
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        ...
        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) { //Step 2.處理未被InputReader取走的輸入事件與設備事件
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            ...
            // This must be an input event
            if (eventItem.events & EPOLLIN) {
                int32_t readSize =
                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);//Step 3.讀取底層上報事件
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    ...
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {//構建需要上報的事件
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;//將event指針移動到下一個可用于填充事件的RawEvent對象
                        capacity -= 1;
                    }
                    ...
                }
            } 
            ...
        }
        ...
        mLock.unlock(); // release lock before poll
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//Step 4.阻塞,等待事件各種類型消息
        mLock.lock(); // reacquire lock after poll
        ...
    }
    // All done, return the number of events we read.
    return event - buffer;
}

Step 1. 掃描設備,會獲取input/dev/下的所有設備,并將各個設備注冊到epoll線程池里,監聽各個設備的消息狀態;
Step 2. 處理未被InputReader取走的輸入事件與設備事件,一般情況下有事件上報時,epoll_wait會讀取到mPendingEventItems值,即mPendingEventCount值,即會進入該流程;
Step 3. 讀取底層上報事件,根據上報的fd設備,讀取對應的設備節點。即可以獲取到上報的事件內容。如下為屏幕點擊對應的上報事件:

Step 4. 通過epoll機制,阻塞當前進程,等待設備節點變更,事件上報。

4.6.3 Input事件加工

????????主要是將底層RawEvent事件,進一步加工,將Event事件注入到mArgsQueue隊列的過程。
(1)Input事件加工

@frameworks\native\services\inputflinger\reader\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;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            ...
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//輸入事件
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED://設備增加
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED://設備移除
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN://設備掃描結束
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }

}

(2)Input事件推送
該流程業務代碼比較冗長,做了層層封裝,如下為方法調用棧:
InputReader.processEventsLocked() -> InputReader.processEventsForDeviceLocked() -> InputDevice.process() -> MultiTouchInputMapper.process() -> TouchInputMapper.process()->TouchInputMapper.sync() -> TouchInputMapper.processRawTouches() -> TouchInputMapper.cookAndDispatch() -> TouchInputMapper.dispatchTouches() -> TouchInputMapper.dispatchMotion() -> QueuedInputListener -> notifyMotion()
最終可以看到事件最終會傳遞到mArgsQueue容器內。

@frameworks\native\services\inputflinger\InputListener.cpp
std::vector<NotifyArgs*> mArgsQueue;
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyMotionArgs(*args));
}

4.6.4 事件發布

(1)當事件加工完成后,會引用flush()方法,將事件發布出去

@frameworks\native\services\inputflinger\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();
}

(2)由上一節可知,屏幕點擊事件對應的args為NotifyMotionArgs

@frameworks\native\services\inputflinger\InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);
}

(3)大家可以自己去追溯下源碼,該listener接口的實現類是InputDispatcher。至此,事件將進入下一階段——事件分發。

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ...
}

4.7 Inputdispatcher模塊

????????事件分發服務,將底層讀到的事件,分發到上層

4.7.1 Input事件上報

????????至此,我們知道InputDispatch會啟動一個阻塞線程,等待底層事件上報;而通過InputReader的分析,我們知道底層事件響應,最終會通知InputDispatch模塊的notifyMotion()方法

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ...
    { // acquire lock
        mLock.lock();
        ...
        // Just enqueue a new motion event.
        MotionEntry* newEntry =
                new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
                                args->displayId, policyFlags, args->action, args->actionButton,
                                args->flags, args->metaState, args->buttonState,
                                args->classification, args->edgeFlags, args->xPrecision,
                                args->yPrecision, args->xCursorPosition, args->yCursorPosition,
                                args->downTime, args->pointerCount, args->pointerProperties,
                                args->pointerCoords, 0, 0);

        needWake = enqueueInboundEventLocked(newEntry);//構建新的MotionEvent事件
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();//喚醒InputDispatch線程,進行分發
    }
}

4.7.2 啟動InputDispatcher線程

(1)創建一個InputDispatcher線程

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
    return OK;
}

(2)InputThread線程的loop隊列

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);//事件分發
        }
        ...
    } // 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);//堵塞,等待喚醒
}

(3)事件分發過程
事件的分發過程也比較冗長,此處不具體分析過程,其業務堆棧如下,即事件分發最終會下發到publishMotionEvent。
InputDispatcher.dispatchOnceInnerLocked() -> InputDispatcher.dispatchMotionLocked() -> InputDispatcher.dispatchEventLocked() -> InputDispatcher.prepareDispatchCycleLocked() -> InputDispatcher.enqueueDispatchEntriesLocked() -> InputDispatcher.startDispatchCycleLocked() -> InputPublisher.publishMotionEvent()

@frameworks\native\libs\input\InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
        uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
        std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
        int32_t edgeFlags, int32_t metaState, int32_t buttonState,
        MotionClassification classification, float xScale, float yScale, float xOffset,
        float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
        float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
    ...
    InputMessage msg;
    msg.header.type = InputMessage::Type::MOTION;
    msg.body.motion.seq = seq;
    msg.body.motion.eventId = eventId;
    ...
    return mChannel->sendMessage(&msg);
}

4.8 WindowManagerService模塊

4.8.1 ViewRootImpl階段

????????InputDispatcher通過InputChannel將事件發送到目標窗口的進程了。那么目標窗口是如何接收傳遞事件呢?
(1)Activity創建窗口相關階段介紹
attach階段:
一個Activity 創建了一個PhoneWindow對象 ,PhoneWindow通過setWindowManager() 創建了WindowManagerImpl 。
即Activity 對應一個PhoneWindow,并得到了一個WindowManager(WindowManagerImpl,Window創建的)。
onCreate階段:
創建了DecorView ,并將 activity的布局添加到DecorView中 。
onResume階段:
創建了ViewRootImpl,通過setView()最終由Session進入system_server進程。最終執行addWindow添加窗口到WMS。

(2)ViewRootImpl.setView()

@frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                ...
                InputChannel inputChannel = null;
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    inputChannel = new InputChannel();//創建inputChannel對象
                }
                try {
                    ...
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);//通過session跨進程調用WMS的addWindow方法給inputChannel賦值
                    setFrame(mTmpFrame);
                }
                ...
                if (inputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
                            Looper.myLooper());//創建mInputEventReceiver對象,用于App側接收Input事件
                }
                ...
            }
        }
    }

4.8.2 WindowManagerService.addWindow()

(1)openInputChannel():生成一對inputChannel,并返回一個對象給App端。
Session.addToDisplayAsUser() -> WindowManagerService.addWindow() -> EmbeddedWindow.openInputChannel()

@frameworks\base\services\core\java\com\android\server\wm\EmbeddedWindowController.java
InputChannel openInputChannel() {
    final String name = getName();

    final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//InputChannel底層通過一對socket進行通信
    mInputChannel = inputChannels[0];
    final InputChannel clientChannel = inputChannels[1];
    mWmService.mInputManager.registerInputChannel(mInputChannel);//將一個inputChannel對象注冊到Input的Native端
    ...
    return clientChannel;//返回一個inputChannel對象給App端
}

(2)openInputChannelPair():創建一對通過socket通信的inputChannel對象。
InputChannel.openInputChannelPair() -> InputChannel.nativeOpenInputChannelPair() -> android_view_InputChannel.android_view_InputChannel_nativeOpenInputChannelPair() -> InputTransport.openInputChannelPair()

@frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.c_str(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    sp<IBinder> token = new BBinder();

    std::string serverChannelName = name + " (server)";
    android::base::unique_fd serverFd(sockets[0]);
    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);//server端

    std::string clientChannelName = name + " (client)";
    android::base::unique_fd clientFd(sockets[1]);
    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);//client端
    return OK;
}

4.8.3 WindowInputEventReceiver

????????app進程和system_server進程通過socket通信,底層捕獲的事件最終通過inputChannel模塊來實現,再由app端的WindowInputEventReceiver去接收,最后把事件分發到目標View上。
(1)WindowInputEventReceiver構造函數
注冊一個事件接收器,WindowInputEventReceiver的父類是InputEventReceiver

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
    ...
    mInputChannel = inputChannel;
    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);//初始化操作

    mCloseGuard.open("dispose");
}

// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
private void dispatchInputEvent(int seq, InputEvent event) {//native層事件回調方法
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event);//事件分發到各個目標View上
}

(2)nativeInit
由上可知,在添加窗口時,WMS會針對于每個窗口設置一對InputChannel對象,分為client端和server端,其中server端在system_server進程,client端在app進程。我們需要去監聽client端,以期能夠捕獲server端的事件消息。

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    ...
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();//初始化
    ...
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();//此fd為WMS創建的InputChannel的client端
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);//注冊監聽
        } else {
            mMessageQueue->getLooper()->removeFd(fd);//移除監聽
        }
    }
}

(3)handleEvent
當server端寫入事件時,client端的looper就能被喚醒,會調用handleEvent函數(當fd可讀時,會調用LooperCallback的handleEvent,而NativeInputEventReceiver繼承自LooperCallback,所以這里會調用NativeInputEventReceiver的handleEvent函數,線程和looper的關系此處不展開)

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ...
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);//處理事件
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
    ...
    return 1;
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...
    for (;;) {
        ...
        if (!skipCallbacks) {
            ...
            if (inputEventObj) {
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);//事件消息回調java層
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
            }
        }
        ...
    }
}

五、Input設備節點介紹

5.1 常見觸摸事件類型

事件類型 事件名稱 事件編碼 事件定義
EV_SYN 同步事件 0004 or 0005 代表一個事件開始(不必要)
EV_SYN 同步事件 SYN_REPORT 代表一個事件結束(必要的)
EV_ABS 絕對坐標的事件 ABS_MT_SLOT 本質代表著不同的手指,他的value代表手指id
EV_ABS 絕對坐標的事件 ABS_MT_TRACKING_ID 類協議特有的,每個slot會和一個ID相對應,一個非負數表示一次接觸,ffffffff表示一次接觸結束,即手指抬起。無論在接觸的類型相對應的slot發生改變,驅動都應該通過改變這個值來使這個slot失效,并且下一次觸摸的ID值會是這次的值加1
EV_ABS 絕對坐標的事件 ABS_MT_POSITION_X 相對于屏幕中心的x坐標
EV_ABS 絕對坐標的事件 ABS_MT_POSITION_Y 相對于屏幕中心的y坐標
EV_ABS 絕對坐標的事件 ABS_MT_TOUCH_MAJOR 接觸部分的長軸長度,相當于橢圓的長軸
EV_ABS 絕對坐標的事件 ABS_MT_TOUCH_MINOR 接觸部分的短軸長度,相當于橢圓的短軸
EV_ABS 絕對坐標的事件 ABS_MT_PRESSURE 代表按下壓力,有的設備不一定有
EV_KEY 按鍵事件 BTN_TOUCH 觸碰按鍵,其值是DOWN或者UP
EV_KEY 按鍵事件 BTN_TOOL_FINGER 按鍵的是finger,其值是DOWN或者UP

5.2 getevent

adb shell getevent -lt

5.3 sendevent

模擬按壓音量鍵+

//通過getevent指令,獲取音量按鍵+的事件碼
bengal:/ # getevent
add device 1: /dev/input/event4
  name:     "bengal-scubaidp-snd-card Button Jack"
add device 2: /dev/input/event3
  name:     "bengal-scubaidp-snd-card Headset Jack"
add device 3: /dev/input/event0
  name:     "qpnp_pon"
add device 4: /dev/input/event1
  name:     "gpio-keys"
add device 5: /dev/input/event2
  name:     "sitronix_ts_i2c"
/dev/input/event1: 0001 0073 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0073 00000000
/dev/input/event1: 0000 0000 00000000

//通過sendevent模擬音量鍵+的事件
130|bengal:/ # sendevent /dev/input/event1 1 115 1
bengal:/ # sendevent /dev/input/event1 0 0 0
bengal:/ # sendevent /dev/input/event1 1 115 0
bengal:/ # sendevent /dev/input/event1 0 0 0
bengal:/ #

ps:getevent獲取到的事件碼為16進制,sendevent輸入的值為10進制,需要注意下!!!

六、參考資料

https://liuwangshu.blog.csdn.net/article/details/84883156
https://liuwangshu.blog.csdn.net/article/details/86771746
https://www.cnblogs.com/brucemengbm/p/7072395.html
事件分發介紹:
https://www.cnblogs.com/fanglongxiang/p/14091511.html
InputChannel介紹:
https://blog.csdn.net/ztisen/article/details/130188132
GetEvent指令介紹:
https://blog.csdn.net/Gary1_Liu/article/details/124675608

總結

以上是生活随笔為你收集整理的Android 输入系统介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。