Touch 事件原理分析 (一)

文章内容主要从 https://segmentfault.com/a/1190000011826846 而来

1,InputManagerService

Android Framework 层的 service,大部分都是在 SystemServer 进程中创建,InputManagerService 即是如此,创建完 Service 后,便把它加入到 ServiceManager 中,并同时设置了 InputMonitor,如下

inputManager = new InputManagerService(context);
WindowManagerService wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
mActivityManagerService.setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();

看一下InputManagerservice 的构造函数

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

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

        String doubleTouchGestureEnablePath = context.getResources().getString(
                R.string.config_doubleTouchGestureEnableFile);
        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
            new File(doubleTouchGestureEnablePath);

        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }

nativeInit 的实现如下

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast(im);
}

首先是获取 MessageQueue, 接着创建 NativeInputManager ,NativeInputManager 的实现如下

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
     ...

    mInteractive = true;
    sp eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager 中创建了 EventHub,并利用 EventHub 创建了InputManger, InputManager 实现如下

InputManager::InputManager(
        const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

代码中创建了 InputDispatcher,接着创建了 InputReader,接着是 initialize 方法

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

分别将 reader 和 dispatcher 封装到相应的 Thread 中,此时对 SystemServer 来说,InputManager 基本创建完成,接着便是 start Service ,对应的 native 方法便是 nativeStart(mPtr)

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast(ptr);

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

2,小结

整体流程如下


整体流程图

InputManger 通过 InputReaderThread 读和处理未加工的输入事件然后分发到 DispatcherThread 队列中, InputDispatcherThread 将接收的队列发送给相应的应用程序

3,epoll

这里首先了解一下 epoll,之前我们处理输入流的时候,我们会对每一个流进行遍历,然后检测到有修改的数据,将其取出来,这其中存在大量的资源消耗,尤其是在流比较多的时候,epoll 便在这里优化,当无数据的时候会阻塞队列,当有数据的时候,只将其中有变化的进行分发。
epoll更详细的分析

4,EventHub

在 NativeInputManager 中,曾经创建的EventHub,并作为参数传递给了 InputManger。EventHub是将不同来源的消息转化为统一类型并交给上层处理。先查看其构造函数

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句柄
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    mINotifyFd = inotify_init();

    //监视dev/input目录的变化删除和创建变化
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;

    //把inotify的句柄加入到epoll监测
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    //创建匿名管道
    int wakeFds[2];
    result = pipe(wakeFds);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    //将管道的读写端设置为非阻塞
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

    eventItem.data.u32 = EPOLL_ID_WAKE;

    //将管道的读端加入到epoll监测
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

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

其中创建了 epoll 句柄、inotify 句柄、匿名管道(非阻塞),inotify负责监控目录和文件的变化,这里监控的是/dev/input 目录。
EventHub 负责监控,相关事件的处理由 ReaderThread 处理。ReaderThread 是继承Android 的 Thread

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

这个方法返回 true , 表示 threadLoop会被循环调用,也就是 loopOnce 会被循环调用,下面是 loopOnce

void InputReader::loopOnce() {
      .....
    //从EventHub中获取事件
    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) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

   //将排队的事件队列发送给监听者,实际上这个监听者就是Input dispatcher
    mQueuedListener->flush();
}

我们要分析的是其中的两个方法 getEvents 和 processEventsLocked

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
     for(;;) {
        ....
      while (mPendingEventIndex < mPendingEventCount) {
         const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
        .....
       ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
     if (eventItem.events & EPOLLIN) {
         int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
        if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
           // 设备被移除,关闭设备
           deviceChanged = true;
           closeDeviceLocked(device);
         } else if (readSize < 0) {
             //无法获得事件
             if (errno != EAGAIN && errno != EINTR) {
                 ALOGW("could not get event (errno=%d)", errno);
             }
         } else if ((readSize % sizeof(struct input_event)) != 0) {
            //获得事件的大小非事件类型整数倍
            ALOGE("could not get event (wrong size: %d)", readSize);
       } 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];
               if (iev.type == EV_MSC) {
                 if (iev.code == MSC_ANDROID_TIME_SEC) {
                     device->timestampOverrideSec = iev.value;
                     continue;
                  } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                     device->timestampOverrideUsec = iev.value;
                     continue;
                  }
               }
              //事件时间相关计算,时间的错误可能会导致ANR和一些bug。这里采取一系列的防范 
               .........
             event->deviceId = deviceId;
             event->type = iev.type;
             event->code = iev.code;
             event->value = iev.value;
             event += 1;
             capacity -= 1;
          }
        if (capacity == 0) {
          //每到我们计算完一个事件,capacity就会减1,如果为0。则表示  结果缓冲区已经满了,
      //需要重置开始读取时间的索引值,来读取下一个事件迭代                    
           mPendingEventIndex -= 1;
           break;
      }
 } 
    //表明读到事件了,跳出循环
    if (event != buffer || awoken) {
            break;
     }
     mPendingEventIndex = 0;
     int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
       if (pollResult == 0) {
          mPendingEventCount = 0;
          break;
       }
      //判断是否有事件发生
       if (pollResult < 0) {
          mPendingEventCount = 0;
        } else {
            //产生的事件的数目
          mPendingEventCount = size_t(pollResult);
        }
    }
    //产生的事件数目
    return event - buffer;
}
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) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
                        || rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            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;
    }
}

getEvents 方法会进行一些新增设备和移除设备的更新操作。至于点击事件是通过指针参数 RawEvent, 其作为起始地址记录事件,在循环体中,处理获取时间、检测相关设备类型、读取事件,如果检测到事件,则跳出循环。更新 mPendingEventCount 和 mPendingEventIndex 来控制事件的读取,epoll_wait 来得到事件的来源。
在 looperOnce 获取到事件后,就会调用 processEventsLocked。其负责事件添加、设备移除等,事件相关还是 processEventsForDeviceLocked 方法,根据事件获取相应的设备类型,并交给相应的设备处理,即 InputMapper 。


InputMapper及其子类

在这里,事件会被 mapper->process 处理

void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);
    }
}
void TouchInputMapper::sync(nsecs_t when) {
    .....
    processRawTouches(false /*timeout*/);
}
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
      ....
    dispatchMotion();
      ....
}
void TouchInputMapper::dispatchMotion() {
   ....
   NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, actionButton, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    getListener()->notifyMotion(&args);
}
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

此时数据被加入到参数队列中,此时回到 loopOnce 中,调用 QueuedInputListener 的 flush 方法

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();
}
void NotifyMotionArgs::notify(const sp& listener) const {
    listener->notifyMotion(this);
}
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    .....
   MotionEvent event;
   event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);
    ....
  MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);
   needWake = enqueueInboundEventLocked(newEntry);
    ....
   if (needWake) {
      mLooper->wake();
   }
}

在 notifyMotion 中将参数包装成 MotionEntry,加入到 enqueueInboundEventLocked 中,然后唤醒 looper。
Dispatcher 下的 dispatchOnce

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
void InputDispatcher::dispatchOnce() {
    ...
   dispatchOnceInnerLocked(&nextWakeupTime);
    ...
}
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
   ....
   mPendingEvent = mInboundQueue.dequeueAtHead();
   ....
   switch (mPendingEvent->type) {
        case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
    ....
   }
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
    EventEntry* eventEntry, const Vector& inputTargets) {
    ....
    pokeUserActivityLocked(eventEntry);
    .....
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } 
    }
}

prepareDispatchCycleLocked 调用 enqueueDispatchEntriesLocked 调用 startDispatchCycleLocked

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp& connection) {
   EventEntry* eventEntry = dispatchEntry->eventEntry;
    ....
   switch (eventEntry->type) {
      ....
    case EventEntry::TYPE_MOTION: {
      status = connection->inputPublisher.publishMotionEvent( ....);    
      break;
    }
    ....
   }
    ...
}
status_t InputPublisher::publishMotionEvent(...) {
  ....
  InputMessage msg;
  msg.header.type = InputMessage::TYPE_MOTION;
  msg.body.motion.seq = seq;
  msg.body.motion.deviceId = deviceId;
  msg.body.motion.source = source;
  msg.body.motion.action = action;
  msg.body.motion.actionButton = actionButton;
  msg.body.motion.flags = flags;
  msg.body.motion.edgeFlags = edgeFlags;
  msg.body.motion.metaState = metaState;
  msg.body.motion.buttonState = buttonState;
  msg.body.motion.xOffset = xOffset;
  msg.body.motion.yOffset = yOffset;
  msg.body.motion.xPrecision = xPrecision;
  msg.body.motion.yPrecision = yPrecision;
  msg.body.motion.downTime = downTime;
  msg.body.motion.eventTime = eventTime;
  msg.body.motion.pointerCount = pointerCount;
  for (uint32_t i = 0; i < pointerCount; i++) {
      msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
      msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
  }
    return mChannel->sendMessage(&msg);
}
具体流程图
整体流程图

ReaderThread 开启后会从EventHub轮训获取时间,获取事件后,经过一系列的封装,通过 InputChannel 发送出去

Touch 事件原理分析 (下)

你可能感兴趣的:(Touch 事件原理分析 (一))