1. 程式人生 > >Android8.0 事件輸入系統

Android8.0 事件輸入系統

Android 輸入子系統名義上是由遍歷系統多個層的事件管道組成,在最低層,物理輸入裝置會生成描述狀態更改(例如按鍵按壓和觸控接觸點)的訊號。裝置韌體以某種方式編碼和傳輸這些訊號,例如向系統傳送 USB HID 報告或在 I2C 總線上產生中斷。 然後,訊號由 Linux 核心中的裝置驅動程式解碼。Linux 核心為許多標準的外圍裝置提供驅動程式,特別是那些符合 HID 協議的外圍裝置。然而,原始裝置製造商 (OEM) 通常必須為在低級別緊密整合到系統的嵌入式裝置(如觸控式螢幕)提供自定義驅動程式。

在這裡插入圖片描述

輸入裝置驅動程式負責通過 Linux 輸入協議將裝置特定訊號轉換為標準輸入事件格式。Linux 輸入協議在 linux/input.h 核心標頭檔案中定義了一組標準事件型別和程式碼。這樣一來,核心之外的元件就不需要關注物理掃描程式碼、HID 用途、I2C 訊息、GPIO 引腳等方面的詳細資訊。 接下來,Android EventHub 元件通過開啟與每個輸入裝置關聯的 evdev 驅動程式從核心讀取輸入事件。然後,Android InputReader 元件根據裝置類別解碼輸入事件,並生成 Android 輸入事件流。

1. 事件輸入管理服務

frameworks\base\services\java\com\android\server\SystemServer.java

private void startOtherServices() {
            ......
            inputManager = new InputManagerService(context); //1.輸入法服務
            wm = WindowManagerService.main(context, inputManager,   //2.窗體管理服務
                    mFactoryTestMode !=
FactoryTest.FACTORY_TEST_LOW_LEVEL, !mFirstBoot, mOnlyCore, new PhoneWindowManager()); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); ...... }

frameworks\base\services\core\java\com\android\server\input\InputManagerService.java

 public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); //處理輸入事件
        ......
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); //初始化
        LocalServices.addService(InputManagerInternal.class, new LocalService());
        ......
    }

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);
}

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();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);
    ......
    sp<EventHub> eventHub = new EventHub(); //事件集線器
    mInputManager = new InputManager(eventHub, this, this); //輸入管理者
}

frameworks\native\services\inputflinger\InputManager.cpp

InputManager::InputManager(
        const sp<InputReaderInterface>& reader,
        const sp<InputDispatcherInterface>& dispatcher) :
        mReader(reader),
        mDispatcher(dispatcher) {
    initialize();
}

frameworks\native\services\inputflinger\InputManager.cpp

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

  public static WindowManagerService main(final Context context, final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
            WindowManagerPolicy policy) {
        DisplayThread.getHandler().runWithScissors(() ->
                sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
                        onlyCore, policy), 0);
        return sInstance;
    }

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java 建立事件輸入通道

private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
            WindowManagerPolicy policy) {
        ......
        if(mInputManager != null) {
            //建立事件輸入通道
            final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
            mPointerEventDispatcher = inputChannel != null
                    ? new PointerEventDispatcher(inputChannel) : null;
        } else {
            mPointerEventDispatcher = null;
        }
       ......
    }

frameworks\base\services\core\java\com\android\server\input\InputManagerService.java 建立Channel並由com_android_server_input_InputManagerService註冊

 public InputChannel monitorInput(String inputChannelName) {
        //建立並註冊通道
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
        nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
        inputChannels[0].dispose(); // don't need to retain the Java object reference
        return inputChannels[1];
    }

frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    //新建通道
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    //新建處理控制代碼
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
     //註冊通道和處理控制代碼
    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
}

frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp 最終呼叫InputDispatch去註冊輸入通道

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, inputWindowHandle, monitor);
}

frameworks\native\services\inputflinger\InputDispatcher.cpp 實際在派發器中註冊, Connection是InputDispatcher內部的一個類結構

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    { // acquire lock
        AutoMutex _l(mLock);
        //合成一個通道連線
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection); //fd對映

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); //加入Looper
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

2. 輸入事件採集

frameworks\base\services\core\java\com\android\server\input\InputManagerService.java

 public void start() { ...... nativeStart(mPtr); ......}

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();
}

frameworks\native\services\inputflinger\InputManager.cpp

status_t InputManager::start() { 
    //啟動事件派發執行緒
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    //啟動事件採集執行緒
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

    return OK;
}

frameworks\native\services\inputflinger\InputReader.cpp

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

frameworks\native\services\inputflinger\InputReader.cpp

void InputReader::loopOnce() {
    ......
    
    //採集輸入事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast(); 

        if (count) {
            processEventsLocked(mEventBuffer, count); //處理事件
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) { //超時
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices); //獲取輸入裝置
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices); //通知輸入裝置
    }

    //重新整理事件監聽佇列
    mQueuedListener->flush();
}

frameworks\native\services\inputflinger\EventHub.cpp epoll 是Linux下多路複用IO介面select/poll的增強版本,它能顯著提高程式在大量併發連線中只有少量活躍的情況下的系統CPU利用率。另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被核心IO事件非同步喚醒而加入Ready佇列的描述符集合就行了。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得使用者空間程式有可能快取IO狀態,減少epoll_wait/epoll_pwait的呼叫,提高應用程式效率。 Inotify 是一個 Linux 核心特性,它監控檔案系統,並且及時向專門的應用程式發出相關的事件警告,比如刪除、讀、寫和解除安裝操作等。

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //建立EPOLL,多路IO複用
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
    //建立Inotify
    mINotifyFd = inotify_init();
    //觀察檔案描述符
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN; //事件
    eventItem.data.u32 = EPOLL_ID_INOTIFY;ea
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0]; //讀喚醒描述符
    mWakeWritePipeFd = wakeFds[1]; // 寫喚醒描述符

    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);

    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);

    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kernel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

frameworks\native\services\inputflinger\EventHub.cpp 獲取系統輸入原始事件

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer; //原始事件
    size_t capacity = bufferSize; //緩衝容量
    bool awoken = false;
    for (;;) { //開始迴圈
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        .......
        // Report any devices that had last been added/removed.
        //裝置刪減 
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        //掃描裝置
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

        ......

        // Grab the next input event.
        //獲取下一次輸入事件
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) { //喚醒
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); //讀取資料
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                <