美文网首页
按键 | InputManagerService

按键 | InputManagerService

作者: 力卉编程 | 来源:发表于2019-12-28 21:13 被阅读0次

本文主要讲一下inputmanager相关的,即驱动把数据上报到用户空间后,用户空间到应用这么个流程,
内核的input子系统,采取的反向分析,即由驱动出发,最后到input core,input子系统架构这么个由点到面的分析方法,
那分析inputmanager是否可采用这种方法如何呢?实际上,对于Android上层(Native framework/framework, c++/java)的分析,一般采用的是由上而下的分析,即从其初始化(main,构造,onFirstRef())开始, 通常在其初始化时候,会重一些很重要的上下层的连接,如果由下往上看,会麻烦点。

input到应用的流程为
  • EventHub监控并读取/dev/input下数据
  • 给InputReader 加工处理
  • 到InputDispacher
  • 找到focused窗口并通过input channel发出去
相关代码目录:

Android 9.0 http://androidxref.com/9.0.0_r3/
frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
frameworks/native/services/inputflinger/

1.1 初始化
//frameworks/base/services/java/com/android/server/SystemServer.java
startOtherServices() {
    inputManager = new InputManagerService(context);
    ....
    wm = WindowManagerService.main(context, inputManager,
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    ....
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
    ......
}

IMS(InputManagerService)的初始化,是从SystemServer开始的,通过搜索代码(如上),我们可以看到构造了一个实例,
并做为参数传给了WMS, 由此我们也猜想,会和WMS有紧密的关系,然后
IMS设置了setWindowManagerCallbacks()并通过start()函数启动了,
SystemServer里有关IMS的就这么几个地方,我们再看下构造和start()具体的流程,与WMS的关联不分析。

//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Pointer to native input manager service object.
private final long mPtr;

public InputManagerService(Context context) {
    this.mContext = context;
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

    // config_useDevInputEventForAudioJack配置为true, 耳机事件可通过input上报
    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
......
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
......
    LocalServices.addService(InputManagerInternal.class, new LocalService());
}

public void start() {
    Slog.i(TAG, "Starting input manager");
    nativeStart(mPtr);
....
}

InputManagerService构造和start()主要也是调到JNI的 nativeInit() nativeStart().

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

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
......
    status_t result = im->getInputManager()->start();
......
}

nativeInit()又构造了一个 NativeInputManager(),该类可认为是上层JAVA和下层EventHub InputManager的桥梁,
nativeStart()通过 NativeInputManager最终调到 InputManager 的 start()方法

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
......
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager()的构造又new了 EventHub 和 InputManager , 其中
eventHub做为参数传给了 InputManager()

//frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
......{
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    mEpollFd = epoll_create(EPOLL_SIZE_HINT); // epoll机制
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
    mINotifyFd = inotify_init(); // inotify机制
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); // 利用inotify监控 DEVICE_PATH(/dev/input)创建和删除
......
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 将inotify的fd添加到Epoll监控中
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
    int wakeFds[2];
    result = pipe(wakeFds); //读写pipe, InputReader有事件时唤醒
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
......
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
......
}

EventHub相当于一个集线器,把底层的USB, TOUCH,鼠标等事件统一收集上来,再给上层。
其构造函数当中利用inotify机制监控"/dev/input" 目录下设备的创建和删除,这样当有设备变更时就可以收到通知了,
构造函数也创建了所需要的mEpollFd,这个作为IO多路复用的机制,不清楚的可以查下如何使用,
构造里将mINotifyFd添加到了epoll里,在后续input设备创建的时候,也会把input设备的fd添加进去,这样当有数据或者设备变化时,
EventHub就可获取这些事件,进一步处理。
构造还创建了两个pipe,作为wakeup的读端和写端,当InputReader.cpp有事件(配置变更,monitor, 超时请求等)唤醒EventHub处理。

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher); // eventHub又传给了 InputReader,最终他们俩是紧密联系在一起的
    initialize(); // eventHub又传给了
}

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

InputManager(),创建了InputDispatcher和InputReader实例并与对应的InputDispatcherThread InputReaderThread 线程关联
具体的我们不往下跟了,有兴趣的可以再看看,
至此,初始化流程告一段落。

InputManagerService.java的 start方法,最终到InputManager::start(),

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
......
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
......
}

start() 方法目的就是让这两个线程跑起来,这样就可以不断的获取,处理消息了。

1.2 小结
startOtherServices()/SystemServer.java
    + new InputManagerService(context) --> nativeInit(...) --> new NativeInputManager(...)
    +                                                              + new EventHub() --> inotify监控/dev/input + epoll + wake pipe
    +                                                              + new InputManager(eventHub,...)
    +                                                                  +  new InputDispatcher()
    +                                                                  +  new InputReader(eventHub,...)
    +                                                                  +  initialize()
    +                                                                          + new InputReaderThread(mReader)
    +                                                                          + new InputDispatcherThread(mDispatcher)
    +
    +                                                                                          ^
    +                                                                                          +
    + inputManager.start() --> nativeStart(mPtr) --> im->getInputManager()->start() --> mDispatcherThread->run() mReaderThread->run()
EventHub->getEvents() 集线器处理(按键等事件)
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
......
   for (;;) {
       nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

       // Reopen input devices if needed.
......
       // Report any devices that had last been added/removed.
       while (mClosingDevices) {
......
       }

       // 扫描设备
       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++];
......
           ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
......
           Device* device = mDevices.valueAt(deviceIndex);
           if (eventItem.events & EPOLLIN) { //epoll事件
               // 读取数据
               int32_t readSize = read(device->fd, readBuffer,
                       sizeof(struct input_event) * capacity);
......
                       event->deviceId = deviceId; // <-- 设备id
                       event->type = iev.type;
                       event->code = iev.code;
                       event->value = iev.value;
                       event += 1;
......
       // Return now if we have collected any events or if we were explicitly awoken.
       if (event != buffer || awoken) {
           break;
       }

       // Poll for events.  Mind the wake lock dance!
......
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
......
    return event - buffer;
}

getEvents()会检查是否需要扫描设备,如果需要的话,则会建立设备KeyedVector向量表,
之后若有数据到来则通过read()函数读取数据, 返回RawEvent* buffer给processEventsLocked()进行下一步处理,
若啥事都没有通过epoll_wait()阻塞等待。

本来数据的读取(read())比较简单, 这里只列下设备扫描流程。

EventHub::scanDevicesLocked() --> scanDirLocked(DEVICE_PATH) "/dev/input" --> while处理 openDeviceLocked() -->

status_t EventHub::openDeviceLocked(const char *devicePath) {
......
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
......//一大堆ioctl的信息获取
    if(ioctl(......)) {
......//生成唯一的 deviceId,和device, 做为mdevices的 key, value. 以后的操作会常用到这个deviceId
    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
......
    // Load the configuration file for the device.
    // 加载这个设备的 idc(Input Device Configuration)配置文件
    loadConfigurationLocked(device);

......//能力获取和分类
    // Figure out the kinds of events the device reports.
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
...... // 设备分类
        device->classes |= ......;
......
    //加入到epoll当中
    if (registerDeviceForEpollLocked(device) != OK) {
......
    configureFd(device);
......//加入mDevices并更新 mOpeningDevices 链表
    addDeviceLocked(device);
    return OK;
}
// 对于我们的触屏来说class为
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
        && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
......
    if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
        device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
    }
......//之后还会加载虚拟key.
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
    // Load the virtual keys for the touch screen, if any.
    // We do this now so that we can make sure to load the keymap if necessary.
    status_t status = loadVirtualKeyMapLocked(device);

status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
    // The virtual key map is supplied by the kernel as a system board property file.
......
    path.append("/sys/board_properties/virtualkeys.");
    path.append(device->identifier.name);
......
    return VirtualKeyMap::load(path, &device->virtualKeyMap);

addDeviceLocked()即添加到

EventHub.h KeyedVector<int32_t, Device*> mDevices;

并更新链表mOpeningDevices

EventHub.h KeyedVector<int32_t, Device*> mDevices;
InputReader.h KeyedVector<int32_t, InputDevice*> mDevices;

上面可看到两者value类型不同,他们之间的key 即deviceID是相同的,
其实我个人认为EventHub中的Device为设备的本身属性,是下层设备的实例化,
而InputReader中的InputDevice为更高层次的抽象,主要用于往上层处理数据,
addDeviceLocked()过程中还会根据input设备的不同属性设置不同的Mapper事件转换器。

我们先看下processEventsForDeviceLocked()过程:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
......
    InputDevice* device = mDevices.valueAt(deviceIndex);
......
    device->process(rawEvents, count);
}

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
......
    // 可能会有多个mapper
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
......
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
......
}

processEventsForDeviceLocked() --> device->process() --> mapper->process()
最终数据的处理也是通过mapper来处理的,所以我们还得看下mapper咋添加的

Mapper添加
mapper的添加是根据分类来添加的, 以触屏为例

//frameworks/native/services/inputflinger/InputReader.cpp
InputReader::processEventsLocked() --> addDeviceLocked() --> createDeviceLocked() --> 
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
    device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
    device->addMapper(new SingleTouchInputMapper(device));
}
  1. 分发数据
    在开头也讲到,InputDispatcherThread里不断的loop,调用dispatchOnce()进行数据的分发。
//frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
......
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            // 如果命令列队为空, 进行事件分发
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
    //如果looper里没有信息,会阻塞,直到timeoutMillis超时
    mLooper->pollOnce(timeoutMillis);
}

input monitor笔记

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(......) {
......
    if(mInputManager != null) {
        final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
        mPointerEventDispatcher = inputChannel != null
                ? new PointerEventDispatcher(inputChannel) : null;

@Override
public void registerPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.registerInputEventListener(listener);
}

@Override
public void unregisterPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.unregisterInputEventListener(listener);
}
frameworks/native/libs/input/InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
}
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......          // addToDisplay() 将调用WMS mService.addWindow()
}
WindowInputEventReceiver() --> InputEventReceiver.java InputEventReceiver() --> nativeInit() -->
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
android_view_InputEventReceiver.cpp

完~~
文 |力卉编程

相关文章

网友评论

      本文标题:按键 | InputManagerService

      本文链接:https://www.haomeiwen.com/subject/iyqeoctx.html