frameworks 之InputReader

frameworks 之InputReader

  • InputManagerService 初始化
  • InputManagerService 启动
  • InputReader 事件的读取
    • 设备节点注册和监听
    • 设备输入事件的读取
  • InputReader 事件的处理
    • 设备的添加和删除处理
    • 触摸事件的处理
    • 数据的加工和分发

android 输入事件 主要分 2个流程 事件读取事件分发。本文讲解事件输入流程。
涉及到的类如下
-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/InputManager.cpp
frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
frameworks/native/services/inputflinger/reader/EventHub.cpp
frameworks/native/services/inputflinger/reader/InputReader.cpp
frameworks/native/services/inputflinger/InputThread.cpp
frameworks/native/services/inputflinger/reader/include/EventHub.h
frameworks/native/services/inputflinger/reader/InputDevice.cpp
frameworks/native/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
system/core/libutils/include/utils/BitSet.h

InputManagerService 初始化

SystemService启动时候,启动了一系列服务包括 InputManagerService 。在 startOtherServices 方法中 ,初始化了该服务。

// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...InputManagerService inputManager = null;...t.traceBegin("StartInputManagerService");// 初始化服务inputManager = new InputManagerService(context);t.traceEnd();
}

查看该类构造函数,里面最重要为调用了 nativeInit 接口。初始化相关数据,该方法为 c++ 方法。

// 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());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());String doubleTouchGestureEnablePath = context.getResources().getString(R.string.config_doubleTouchGestureEnableFile);mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :new File(doubleTouchGestureEnablePath);// 只能同进程使用LocalServices.addService(InputManagerInternal.class, new LocalService());}

nativeInit 方法初始化了 NativeInputManager 并把对应的指针地址转化为long类型返回。

// 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);if (messageQueue == nullptr) {jniThrowRuntimeException(env, "MessageQueue is not initialized.");return 0;}// 初始化NativeInputManagerNativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());im->incStrong(0);// 将im指针转换为jlong类型,返回给java 后续其他对象可通过该值获取 NativeInputManagerreturn reinterpret_cast<jlong>(im);
}

而 NativeInputManager 对应的构造函数又初始化了 InputManager,传入当前对象。

// 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();mServiceObj = env->NewGlobalRef(serviceObj);{AutoMutex _l(mLock);mLocked.systemUiLightsOut = false;mLocked.pointerSpeed = 0;mLocked.pointerGesturesEnabled = true;mLocked.showTouches = false;mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;}mInteractive = true;// 初始化 InputManagerInputManager* im = new InputManager(this, this);mInputManager = im;// 添加该服务到 sm 中 inputflingerdefaultServiceManager()->addService(String16("inputflinger"), im);
}

InputManager 里又初始化了 InputDispatcher, InputClassifier,InputReader 等3个类。然后将 对应 InputDispatcher 作为参数传入 InputClassifier, 而 InputClassifier 又作为参数传入 InputReader里。所以可知 InputClassifier 作为 InputDispatcher 和 InputReader的中间沟通桥梁。事件读取后 通过 InputClassifier 通知 InputDispatcher 进行分发

// frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {// 通过 InputDispatcherFactory 创建 InputDispatchermDispatcher = createInputDispatcher(dispatcherPolicy);// 将 dispatcher 传入 InputClassifiermClassifier = new InputClassifier(mDispatcher);// 通过 InputReaderFactory 创建 readermReader = createInputReader(readerPolicy, mClassifier);
}// frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {return new InputReader(std::make_unique<EventHub>(), policy, listener);
}// frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {return new android::inputdispatcher::InputDispatcher(policy);
}

而 InputReaderFactory 对 inputReader 的构造方法中,又初始化了 EventHub 类,作为参数传入,后续该类作为读取节点数据的作用。 EventHub 构造函数中 初始化了 Epoll, 并添加对应dev/input 文件夹删除和添加监听,和 getEvents 一样。

// frameworks/native/services/inputflinger/reader/EventHub.cpp
EventHub::EventHub(void): mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),mNextDeviceId(1),mControllerNumbers(),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {ensureProcessCanBlockSuspend();// 创建epollmEpollFd = epoll_create1(EPOLL_CLOEXEC);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));// 跟getEvent一样 创建对应文件夹mINotifyFd = inotify_init();// 添加对dev/input增加和删除监听mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,strerror(errno));if (isV4lScanningEnabled()) {mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",VIDEO_DEVICE_PATH, strerror(errno));} else {mVideoWd = -1;ALOGI("Video device scanning disabled");}struct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP;eventItem.data.fd = mINotifyFd;// 添加到epoll监听中int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);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.fd = mWakeReadPipeFd;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);
}

总结 InputMangerService 会初始化一系列的变量。包含读取和分发相关类。并监听对应输入文件夹的变化。

InputManagerService 启动

初始化,就会启动对应的事件读取,开启一个循环不断进行读取对应的节点事件。
InputMangerService 通过start 方法进行开启

// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {t.traceBegin("StartInputManager");inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());// 通过start 启动事件监听循环inputManager.start();t.traceEnd();
}

进入start 方法查看,该方法主要进行相关配置的读取和监听,其中最重要的就是 nativeStart 方法。

public void start() {Slog.i(TAG, "Starting input manager");nativeStart(mPtr);// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);// 注册相关触摸监听设置registerPointerSpeedSettingObserver();...updateBlockUntrustedTouchesModeFromSettings();}

查看 nativeStart 方法,该方法将传入的指针做转换为 NativeInputManager,并调用 里面 InputManger 的 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);// 调用 NativeInputManager 里面的 inputManger 的start 方法status_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}

进入 start 方法,该方法 分别调用 inputReader 和 InputDispatcher 的 start 方法开启线程。

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

以 inputReader 为例, 方法第一次 创建了 InputThread类,并将 loopOnce 函数作为参数传入 InputThread里

// frameworks/native/services/inputflinger/reader/InputReader.cpp
status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}// 创建 InputThread 线程, 传入参数为 loopOnce 函数,等于里面 run 执行threadLoop方法时候会执行该函数mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;
}

InputThread 构造参数中, 又创建了线程类 InputThreadImpl 并通过 run 方法启动线程

// frameworks/native/services/inputflinger/InputThread.cpp
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {mThread = new InputThreadImpl(loop);// 创建后 执行run方法mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

启动线程类后 会执行对应的 threadLoop 方法,而 threadLoop 又会执行 mThreadLoop 函数,该参数就是 InputRead 的 loopOnce 函数。这样就等于 loopOnce 函数循环读取对应设备节点事件,并处理。

class InputThreadImpl : public Thread {
public:explicit InputThreadImpl(std::function<void()> loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::function<void()> mThreadLoop;bool threadLoop() override {mThreadLoop();return true;}
};

总结 通过 start 方法 一步步调用到 inputReader 等start 方法并创建对应的线程类开始运作读取底层事件。

InputReader 事件的读取

启动线程后,会一直执行对应的 loopOnce 函数。该函数一开始判断对应的配置是否改变,改变则刷新,并通过 EventHub 的 getEvents 方法读取事件。

// frameworks/native/services/inputflinger/reader/InputReader.cpp
void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;{ // acquire lockstd::scoped_lock _l(mLock);oldGeneration = mGeneration;timeoutMillis = -1;uint32_t changes = mConfigurationChangesToRefresh;if (changes) {mConfigurationChangesToRefresh = 0;timeoutMillis = 0;// 设置包含是否不包含节点设备refreshConfigurationLocked(changes);} else if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);}} // release lock// 读取事件,循环第一次为加载对应的设备节点,后面读取设备节点的消息size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();// 如果有事件if (count) {// 解析对应的事件processEventsLocked(mEventBuffer, count);}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTSALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endifmNextTimeout = LLONG_MAX;timeoutExpiredLocked(now);}}if (oldGeneration != mGeneration) {inputDevicesChanged = true;inputDevices = getInputDevicesLocked();}} // release lock// Send out a message that the describes the changed input devices.if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}// Flush queued events out to the listener.// This must happen outside of the lock because the listener could potentially call// back into the InputReader's methods, such as getScanCodeState, or become blocked// on another thread similarly waiting to acquire the InputReader lock thereby// resulting in a deadlock.  This situation is actually quite plausible because the// listener is actually the input dispatcher, which calls into the window manager,// which occasionally calls into the input reader.// 读取完从队列调用通知mQueuedListener->flush();
}

进入 getEvents 方法。这里主要有个路径,如输入设备变化的注册和监听,已经设备事件输入的读取 。方法传入了 RawEvent数组 该数组和监听事件的 input_event 数组一样大小。

设备节点注册和监听

因为默认 mNeedToReopenDevices 为 false。所以第一个判断不会进入,接下来会判断是否有关闭的设备有的话 将创建对应的 RawEvent 对象 type 为 DEVICE_REMOVED。接下来 因为默认的 mNeedToScanDevices 为 true,所以会调用对应 的 scanDevicesLocked 对设备进行扫描。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {ALOG_ASSERT(bufferSize >= 1);std::scoped_lock _l(mLock);// 跟 RawEvent 一样的大小struct input_event readBuffer[bufferSize];RawEvent* event = buffer;size_t capacity = bufferSize;bool awoken = false;for (;;) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);// Reopen input devices if needed.// 默认为false, 第一次会将 mNeedToScanDevices 变为trueif (mNeedToReopenDevices) {mNeedToReopenDevices = false;ALOGI("Reopening all input devices due to a configuration change.");closeAllDevicesLocked();mNeedToScanDevices = true;break; // return to the caller before we actually rescan}// Report any devices that had last been added/removed.// 判断列表里面是否有关闭的设备,有的话向event数组添加对应的事件for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {std::unique_ptr<Device> device = std::move(*it);ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());event->when = now;event->deviceId = (device->id == mBuiltInKeyboardId)? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID: device->id;event->type = DEVICE_REMOVED;event += 1;// 容器中移除当前迭代器 it 指向的元素,并更新迭代器 it 到下一个元素it = mClosingDevices.erase(it);mNeedToSendFinishedDeviceScan = true;if (--capacity == 0) {break;}}// 第一次为true, 通过 scanDevicesLocked 将对应的设备节点数据处理后放到 mOpeningDevices 数组中if (mNeedToScanDevices) {mNeedToScanDevices = false;scanDevicesLocked();mNeedToSendFinishedDeviceScan = true;}// 第一次不为空,会进入while (!mOpeningDevices.empty()) {std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());mOpeningDevices.pop_back();ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());// 添加 对应的 eventevent->when = now;event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;event->type = DEVICE_ADDED;event += 1;// Try to find a matching video device by comparing device namesfor (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();it++) {std::unique_ptr<TouchVideoDevice>& videoDevice = *it;if (tryAddVideoDeviceLocked(*device, videoDevice)) {// videoDevice was transferred to 'device'it = mUnattachedVideoDevices.erase(it);break;}}// 将对应 device 放到 mDevices map中auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));if (!inserted) {ALOGW("Device id %d exists, replaced.", device->id);}mNeedToSendFinishedDeviceScan = true;if (--capacity == 0) {break;}}// 有新增设备的时候,添加 finish事件if (mNeedToSendFinishedDeviceScan) {mNeedToSendFinishedDeviceScan = false;event->when = now;event->type = FINISHED_DEVICE_SCAN;event += 1;if (--capacity == 0) {break;}}...// Report added or removed devices immediately.// 有设备变化则马上返回if (deviceChanged) {continue;}// Return now if we have collected any events or if we were explicitly awoken.// 如果有任何的事件则马上退出循环if (event != buffer || awoken) {break;}...}// All done, return the number of events we read.return event - buffer;
}

scanDevicesLocked 又会通过对 scanDirLocked 方法进行扫描, 传入的 DEVICE_PATH 为 dev/input

// frameworks/native/services/inputflinger/reader/EventHub.cpp
void EventHub::scanDevicesLocked() {// dev/input 文件夹下status_t result = scanDirLocked(DEVICE_PATH);if (result < 0) {ALOGE("scan dir failed for %s", DEVICE_PATH);}if (isV4lScanningEnabled()) {result = scanVideoDirLocked(VIDEO_DEVICE_PATH);if (result != OK) {ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH);}}if (mDevices.find(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) == mDevices.end()) {createVirtualKeyboardLocked();}
}

scanDirLocked 通过 std::filesystem::directory_iterator 开始遍历。遍历到就调用 openDeviceLocked 方法

// 遍历文件夹下面的文件
status_t EventHub::scanDirLocked(const std::string& dirname) {for (const auto& entry : std::filesystem::directory_iterator(dirname)) {openDeviceLocked(entry.path());}return 0;
}

openDeviceLocked 方法,主要通过 open 加载对应节点文件符,并通过 ioctl 获取对应的设备信息,并将信息放到 InputDeviceIdentifier 实体类中,最终将 InputDeviceIdentifier 作为属性 放到 Device 实体类中, 在调用 registerDeviceForEpollLocked 注册到 epoll 中,最后通过 addDeviceLocked 放到 mOpeningDevices 数组中。

void EventHub::openDeviceLocked(const std::string& devicePath) {...// 判断是否已注册 注册就不用for (const auto& [deviceId, device] : mDevices) {if (device->path == devicePath) {return; // device was already registered}}char buffer[80];ALOGV("Opening device: %s", devicePath.c_str());// 打开对应的节点int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);if (fd < 0) {ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno));return;}// 将数据放到 InputDeviceIdentifier 实体类InputDeviceIdentifier identifier;// Get device name.// 获取名称if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno));} else {buffer[sizeof(buffer) - 1] = '\0';identifier.name = buffer;}...int32_t deviceId = mNextDeviceId++;// 生成 device实体类std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier);...// 添加到 epoll 中if (registerDeviceForEpollLocked(*device) != OK) {return;}device->configureFd();...// 添加到 mOpeningDevices 中addDeviceLocked(std::move(device));
}
status_t EventHub::registerDeviceForEpollLocked(Device& device) {status_t result = registerFdForEpoll(device.fd);if (result != OK) {ALOGE("Could not add input device fd to epoll for device %" PRId32, device.id);return result;}if (device.videoDevice) {registerVideoDeviceForEpollLocked(*device.videoDevice);}return result;
}status_t EventHub::registerFdForEpoll(int fd) {// TODO(b/121395353) - consider adding EPOLLRDHUPstruct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP;eventItem.data.fd = fd;if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {ALOGE("Could not add fd to epoll instance: %s", strerror(errno));return -errno;}return OK;
}
// frameworks/native/services/inputflinger/reader/EventHub.cpp
void EventHub::addDeviceLocked(std::unique_ptr<Device> device) {reportDeviceAddedForStatisticsLocked(device->identifier, device->classes);mOpeningDevices.push_back(std::move(device));
}

扫描完成后,添加到 mOpeningDevices 数组,即该数组不为空,回到 getEvents 方法中 则会进入判断,遍历对应的数组,并添加 RawEvent事件对应的,type为 DEVICE_ADDED。并将对应devvice 放到对应 map中。最后因为有设备添加又会添加 FINISHED_DEVICE_SCAN 事件。因为事件和数组不相等会进入break,退出循环。这样设备节点的添加和监听就完成。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {....while (!mOpeningDevices.empty()) {std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());mOpeningDevices.pop_back();ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());// 添加 对应的 eventevent->when = now;event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;event->type = DEVICE_ADDED;event += 1;// Try to find a matching video device by comparing device namesfor (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();it++) {std::unique_ptr<TouchVideoDevice>& videoDevice = *it;if (tryAddVideoDeviceLocked(*device, videoDevice)) {// videoDevice was transferred to 'device'it = mUnattachedVideoDevices.erase(it);break;}}// 将对应 device 放到 mDevices map中auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));if (!inserted) {ALOGW("Device id %d exists, replaced.", device->id);}mNeedToSendFinishedDeviceScan = true;if (--capacity == 0) {break;}}// 有新增设备的时候,添加 finish事件if (mNeedToSendFinishedDeviceScan) {mNeedToSendFinishedDeviceScan = false;event->when = now;event->type = FINISHED_DEVICE_SCAN;event += 1;if (--capacity == 0) {break;}}...// 如果有任何的事件则马上退出循环if (event != buffer || awoken) {break;}
}

设备输入事件的读取

当添加完又会进入下一个循环,再一次进入 getEvents方法。进入后 即会进入 epoll_wait等待输入事件,当事件返回后,则将对应的数量赋值给 mPendingEventCount ,最后又进入下一次循环。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {...mPendingEventIndex = 0;mLock.unlock(); // release lock before poll// epoll在等待消息产生,产生消息EventItem都会放到 mPendingEventItems 中,会跟mPendingEventCount比较int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);mLock.lock(); // reacquire lock after pollif (pollResult == 0) {// Timed out.mPendingEventCount = 0;break;}if (pollResult < 0) {// An error occurred.mPendingEventCount = 0;// Sleep after errors to avoid locking up the system.// Hopefully the error is transient.if (errno != EINTR) {ALOGW("poll failed (errno=%d)\n", errno);usleep(100000);}} else {// Some events occurred.// 有多少个输入事件mPendingEventCount = size_t(pollResult);}
}

进入下一次循环后, mPendingEventCount 不为0 ,则会大于 mPendingEventIndex 进入事件的读取,判断对应的fd类型,进行相对应的处理。在通过 getDeviceByFdLocked 从 map中获取对应的device。在通过 read 函数读取对应的数据,并判断是否是input_event的整数倍,不是则为异常数据。最后在读取相关的 code,type,value。放到 RawEvent 中。并返回。

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {...// 有相关输入事件的时候走这里bool deviceChanged = false;while (mPendingEventIndex < mPendingEventCount) {const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];if (eventItem.data.fd == mINotifyFd) {if (eventItem.events & EPOLLIN) {mPendingINotify = true;} else {ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);}continue;}if (eventItem.data.fd == mWakeReadPipeFd) {if (eventItem.events & EPOLLIN) {ALOGV("awoken after wake()");awoken = true;char wakeReadBuffer[16];ssize_t nRead;do {nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer));} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer));} else {ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",eventItem.events);}continue;}// 通过文件描述符获取对应的设备节点Device* device = getDeviceByFdLocked(eventItem.data.fd);if (device == nullptr) {ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,eventItem.data.fd);ALOG_ASSERT(!DEBUG);continue;}...// This must be an input event// 输入事件处理if (eventItem.events & EPOLLIN) {int32_t readSize =read(device->fd, readBuffer, sizeof(struct input_event) * capacity);if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {// Device was removed before INotify noticed.ALOGW("could not get event, removed? (fd: %d size: %" PRId32" bufferSize: %zu capacity: %zu errno: %d)\n",device->fd, readSize, bufferSize, capacity, errno);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) {// 一定是inputEvent的整数倍ALOGE("could not get event (wrong size: %d)", readSize);} else {int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;// 一定是inputEvent的整数倍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->readTime = systemTime(SYSTEM_TIME_MONOTONIC);event->deviceId = deviceId;event->type = iev.type;event->code = iev.code;event->value = iev.value;event += 1;capacity -= 1;}if (capacity == 0) {// The result buffer is full.  Reset the pending event index// so we will try to read the device again on the next iteration.mPendingEventIndex -= 1;break;}}} else if (eventItem.events & EPOLLHUP) {ALOGI("Removing device %s due to epoll hang-up event.",device->identifier.name.c_str());deviceChanged = true;closeDeviceLocked(*device);} else {ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,device->identifier.name.c_str());}}
}

总结 事件的读取 通过 getEvents 注册和监听各种事件 并组装成RawEvents实体类返回,如 底层的 input_event 转化为 RawEvents。

InputReader 事件的处理

getEvents 获取后,则要进行事件的处理,processEventsLocked 进行事件的处理。是事件的处理分为 输入事件 和 设备的添加 移除 扫描结束等。

void InputReader::loopOnce() {...size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockstd::scoped_lock _l(mLock);mReaderIsAliveCondition.notify_all();// 如果有事件if (count) {// 解析对应的事件processEventsLocked(mEventBuffer, count);}...} // release lock
}

processEventsLocked 开始依次遍历对应的 rawEvents ,因为 DEVICE_ADDED定义的值很大,所以低于 DEVICE_ADDED 都作为输入事件。

// frameworks/native/services/inputflinger/reader/include/EventHub.h
DEVICE_ADDED = 0x10000000,
// Sent when a device is removed.
DEVICE_REMOVED = 0x20000000,
// Sent when all added/removed devices from the most recent scan have been reported.
// This event is always sent at least once.
FINISHED_DEVICE_SCAN = 0x30000000,FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,// 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;// 输入触摸事件// 比 DEVICE_ADDED 小于都是输入事件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;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endifprocessEventsForDeviceLocked(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 happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}

设备的添加和删除处理

以添加为例,可以看到调用了 addDeviceLocked 方法,该方法 又通过 createDeviceLocked 方法创建对应的 InputDevice 设备 ,将对应的 设备添加 mDevices map中。(这里将getEvents中的 device 又转化为了 InputDevice中)。

void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {if (mDevices.find(eventHubId) != mDevices.end()) {ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);return;}// 从 mDevices 获取对应的device中的 InputDeviceIdentifierInputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);// 创建对应 InputDevice, 添加到 inputReader的 mdevices中,指定了对应mapper 用于后续的事件处理std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);device->configure(when, &mConfig, 0);device->reset(when);if (device->isIgnored()) {ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' ""(ignored non-input device)",device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());} else {ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),device->getSources());}mDevices.emplace(eventHubId, device);// Add device to device to EventHub ids map.const auto mapIt = mDeviceToEventHubIdsMap.find(device);if (mapIt == mDeviceToEventHubIdsMap.end()) {std::vector<int32_t> ids = {eventHubId};mDeviceToEventHubIdsMap.emplace(device, ids);} else {mapIt->second.push_back(eventHubId);}bumpGenerationLocked();if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {notifyExternalStylusPresenceChangedLocked();}// Sensor input device is noisy, to save power disable it by default.// Input device is classified as SENSOR when any sub device is a SENSOR device, check Eventhub// device class to disable SENSOR sub device only.if (mEventHub->getDeviceClasses(eventHubId).test(InputDeviceClass::SENSOR)) {mEventHub->disableDevice(eventHubId);}
}

createDeviceLocked 方法中 最重要的方法是 addEventHubDevice 方法 该方法根据对应的设备类型,添加对应的mapper。而mapper作为后续输入事件对应的处理方法

std::shared_ptr<InputDevice> InputReader::createDeviceLocked(int32_t eventHubId, const InputDeviceIdentifier& identifier) {// 通过 find_if 搜索对应的 看是否有对应的auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&devicePair.second->getDescriptor() == identifier.descriptor;});std::shared_ptr<InputDevice> device;if (deviceIt != mDevices.end()) {device = deviceIt->second;} else {int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),identifier);}device->addEventHubDevice(eventHubId);return device;
}

addEventHubDevice 通过对应的class 判断,这里分析的是多指触摸,所以添加的 mapper 为 MultiTouchInputMapper

// frameworks/native/services/inputflinger/reader/InputDevice.cpp
void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {// 如果存在则不处理if (mDevices.find(eventHubId) != mDevices.end()) {return;}std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses();std::vector<std::unique_ptr<InputMapper>> mappers;// Check if we should skip population...// Touchscreens and touchpad devices.// 如果是触摸的,分多指和单指,大部分都是单指if (classes.test(InputDeviceClass::TOUCH_MT)) {mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));} else if (classes.test(InputDeviceClass::TOUCH)) {mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));}// Joystick-like devices.if (classes.test(InputDeviceClass::JOYSTICK)) {mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));}...// insert the context into the devices setmDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});// Must change generation to flag this device as changedbumpGeneration();
}

总结 添加和删除 将 device转化为 InputDevice,生成的时候 添加对应的事件处理mapper。并方法 mDevices map中,移除则为移除该设备从map中。

触摸事件的处理

触摸事件会将同个设备一系列事件,通过 processEventsForDeviceLocked 进行处理。

// frameworks/native/services/inputflinger/reader/InputReader.cppint32_t deviceId = rawEvent->deviceId;// 遍历处理相同事件或者事件的开始while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endifprocessEventsForDeviceLocked(deviceId, rawEvent, batchSize); // 处理事件

该方法里面又会通过 eventHubId 获取对应的InputDevice,并调用 process 方法进行处理

// frameworks/native/services/inputflinger/reader/InputReader.cpp
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);if (deviceIt == mDevices.end()) {ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);return;}std::shared_ptr<InputDevice>& device = deviceIt->second;// mapper为空,不处理if (device->isIgnored()) {// ALOGD("Discarding event for ignored deviceId %d.", deviceId);return;}device->process(rawEvents, count);
}

InputDevice 的 process 方法又会将之前设备添加 mapper 进行遍历,之前说过多指触摸的mapper 为 MultiTouchInputMapper ,所以会调用到他的 process 方法。

// frameworks/native/services/inputflinger/reader/InputDevice.cpp
void InputDevice::process(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {...} else {for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {// 多指触碰是 MultiTouchMotionAccumulatormapper.process(rawEvent);});}--count;}
}

对应的 process 方法第一步 先进入 TouchInputMapper::process, 但是该方法里面判断为系列事件结束才会进入,所以事件刚开始并不会执行里面逻辑,而是执行 下一个方法。而 mMultiTouchMotionAccumulator.process 则是将对应 type 为 EV_ABS 进行读取 并赋值到 Slot 实体中,最终再由 TouchInputMapper 结束事件统一处理。

// 先执行这个方法
void MultiTouchInputMapper::process(const RawEvent* rawEvent) {// 处理sync SYN_REPORT 事件TouchInputMapper::process(rawEvent);// 处理ABS事件mMultiTouchMotionAccumulator.process(rawEvent);
}

最大支持触摸为16个, 其中 mCurrentSlot 是通过底层驱动给的值并通过 mSlots 中获取对应 Slot 实体类在将拿的值和类型填充到对应的属性。注意如果 ABS_MT_TRACKING_ID 为负数,则表示抬起,所以 mInUse 会被值为 false。

// frameworks/native/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {if (rawEvent->type == EV_ABS) {bool newSlot = false;// 是否符合多指触摸的b协议if (mUsingSlotsProtocol) {// 代表一个触摸点,接下来所有事件属于这个触摸点if (rawEvent->code == ABS_MT_SLOT) {mCurrentSlot = rawEvent->value;newSlot = true;}} else if (mCurrentSlot < 0) {mCurrentSlot = 0;}// 最大支持16个手指if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
#if DEBUG_POINTERSif (newSlot) {ALOGW("MultiTouch device emitted invalid slot index %d but it ""should be between 0 and %zd; ignoring this slot.",mCurrentSlot, mSlotCount - 1);}
#endif} else {Slot* slot = &mSlots[mCurrentSlot];// If mUsingSlotsProtocol is true, it means the raw pointer has axis info of// ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while// updating the slot.if (!mUsingSlotsProtocol) {slot->mInUse = true;}// 根据对应的code 将对应的值 赋值到 slot属性中switch (rawEvent->code) {case ABS_MT_POSITION_X:slot->mAbsMTPositionX = rawEvent->value;warnIfNotInUse(*rawEvent, *slot);break;case ABS_MT_POSITION_Y:slot->mAbsMTPositionY = rawEvent->value;warnIfNotInUse(*rawEvent, *slot);break;case ABS_MT_TOUCH_MAJOR:slot->mAbsMTTouchMajor = rawEvent->value;break;case ABS_MT_TOUCH_MINOR:slot->mAbsMTTouchMinor = rawEvent->value;slot->mHaveAbsMTTouchMinor = true;break;case ABS_MT_WIDTH_MAJOR:slot->mAbsMTWidthMajor = rawEvent->value;break;case ABS_MT_WIDTH_MINOR:slot->mAbsMTWidthMinor = rawEvent->value;slot->mHaveAbsMTWidthMinor = true;break;case ABS_MT_ORIENTATION:slot->mAbsMTOrientation = rawEvent->value;break;case ABS_MT_TRACKING_ID:// 对应ID 如果是 FFFFFFF 小于0,表示抬起if (mUsingSlotsProtocol && rawEvent->value < 0) {// The slot is no longer in use but it retains its previous contents,// which may be reused for subsequent touches.slot->mInUse = false;} else {slot->mInUse = true;slot->mAbsMTTrackingId = rawEvent->value;}break;case ABS_MT_PRESSURE:slot->mAbsMTPressure = rawEvent->value;break;case ABS_MT_DISTANCE:slot->mAbsMTDistance = rawEvent->value;break;case ABS_MT_TOOL_TYPE:slot->mAbsMTToolType = rawEvent->value;slot->mHaveAbsMTToolType = true;break;}}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {// 表示事件结束// MultiTouch Sync: The driver has returned all data for *one* of the pointers.mCurrentSlot += 1;}
}

组装好 slot数据后,就会进入到事件结束标志位 SYN_REPORT,所以会调用 sync 方法。

// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
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, rawEvent->readTime);}
}

进入 sync 方法,会拿出 RawState 对象并进行清空 操作。在调用 syncTouch 方法。该方法即为 MultiTouchInputMapper.cpp 的 syncTouch 方法。

// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {// Push a new state.mRawStatesPending.emplace_back();// 获取rawState,并对变量进行重置RawState& next = mRawStatesPending.back();next.clear();next.when = when;next.readTime = readTime;// Sync button state.next.buttonState =mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();// Sync scrollnext.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();next.rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();mCursorScrollAccumulator.finishSync();// Sync touch// 调用多指 MultiTouchInputMapper的syncTouchsyncTouch(when, &next);// The last RawState is the actually second to last, since we just added a new stateconst RawState& last =mRawStatesPending.size() == 1 ? mCurrentRawState : mRawStatesPending.rbegin()[1];// Assign pointer ids.if (!mHavePointerIds) {assignPointerIds(last, next);}...// 对数据进行加工processRawTouches(false /*timeout*/);
}

回到 MultiTouchInputMapper 中, 该返回获取对应的事件数量,并进行遍历。判断到 isInUse 为 false 表示抬起 不处理。接着从 outState 取出 RawPointerData类,因为上一部已经清空,所以里面不会有内容。并将值赋值给了他。

里面有比较重要的属性是 idToIndex 以及 id。他对应了 ToucheEvent里面的 index 和他对应的value 。由一个32位的int值控制,从高位开始排起,记录每个手指对应的id。

第一步: mPointerIdBits 赋值给临时的 idBits,并通过 clearFirstMarkedBit (先获取对应的前导0,在将该位1值为0),获取到对应的位数后,在从 mPointerTrackingIdMap 判断是否和 trackingId相等 相等则表示id 为n, 不相等则进入下一步

第二步:当id小于0 , 则通过 markFirstUnmarkedBit (对该数值取反得到前导0,在将该位变1),并将前导0 作为该id。

第三步:将对应的 outCount(outCount遍历一次加1) 赋值给index,id赋值给id。并通过markBit(针对哪位变为1) 对 newPointerIdBits 新变量 记录 有手指的标志位 。因为抬起的时候需要将该位变为0,上面只是添加,所以最后在将 newPointerIdBits 赋值给 mPointerIdBits,用于下一次事件的判断。

void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();size_t outCount = 0;BitSet32 newPointerIdBits;mHavePointerIds = true;for (size_t inIndex = 0; inIndex < inCount; inIndex++) {const MultiTouchMotionAccumulator::Slot* inSlot =mMultiTouchMotionAccumulator.getSlot(inIndex);// 抬起为false不处理if (!inSlot->isInUse()) {continue;}if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {std::optional<int32_t> id = getActiveBitId(*inSlot);if (id) {outState->rawPointerData.canceledIdBits.markBit(id.value());}
#if DEBUG_POINTERSALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,getDeviceName().c_str());
#endifcontinue;}if (outCount >= MAX_POINTERS) {
#if DEBUG_POINTERSALOGD("MultiTouch device %s emitted more than maximum of %d pointers; ""ignoring the rest.",getDeviceName().c_str(), MAX_POINTERS);
#endifbreak; // too many fingers!}// 将数据从slot 转化为rawPointerData, 拿出来为空的.outState传进来已被清空RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];outPointer.x = inSlot->getX();outPointer.y = inSlot->getY();outPointer.pressure = inSlot->getPressure();outPointer.touchMajor = inSlot->getTouchMajor();outPointer.touchMinor = inSlot->getTouchMinor();outPointer.toolMajor = inSlot->getToolMajor();outPointer.toolMinor = inSlot->getToolMinor();outPointer.orientation = inSlot->getOrientation();outPointer.distance = inSlot->getDistance();outPointer.tiltX = 0;outPointer.tiltY = 0;outPointer.toolType = inSlot->getToolType();if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {outPointer.toolType = mTouchButtonAccumulator.getToolType();if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;}}bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&(mTouchButtonAccumulator.isHovering() ||(mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0));outPointer.isHovering = isHovering;// Assign pointer id using tracking id if available.// 处理事件 touch 里面 index 以值的关系if (mHavePointerIds) {// 获取对应的IDint32_t trackingId = inSlot->getTrackingId();int32_t id = -1;if (trackingId >= 0) {// 将mPointerIdBits 赋值给idBits,第一次为0,for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {// 拿前导位有多少 然后清空当前的位为0uint32_t n = idBits.clearFirstMarkedBit();// 从map拿出 如果相等则id赋值给他if (mPointerTrackingIdMap[n] == trackingId) {id = n;}}if (id < 0 && !mPointerIdBits.isFull()) {// 如果没找到,则取反返回前导0的位,并将该位置为1,并保存到map中id = mPointerIdBits.markFirstUnmarkedBit();mPointerTrackingIdMap[id] = trackingId;}}if (id < 0) {mHavePointerIds = false;outState->rawPointerData.clearIdBits();newPointerIdBits.clear();} else {// 对应值outPointer.id = id;// 对应touchEvent的 index,当2个手指按下抬起第一个,outCount变为0,id依旧为1outState->rawPointerData.idToIndex[id] = outCount;outState->rawPointerData.markIdBit(id, isHovering);// 标记下新的位数,因为前面mPointerIdBits 不会清除,比如抬起第一个手指 这时候mPointerIdBits 还是1100000// 所以要用新的变量记录最新的手指按下情况newPointerIdBits.markBit(id);}}outCount += 1;}// 记录手指数量outState->rawPointerData.pointerCount = outCount;// 赋值给 newPointerIdBits 这样抬起的位数就变为0mPointerIdBits = newPointerIdBits;mMultiTouchMotionAccumulator.finishSync();
}

对应32位工具类方法的讲解

// system/core/libutils/include/utils/BitSet.h
/** Copyright (C) 2010 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#ifndef UTILS_BITSET_H
#define UTILS_BITSET_H#include <stdint.h>
#include <utils/TypeHelpers.h>/** A class to provide efficient manipulation of bitsets.** Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit* manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to* efficiently perform operations like finding the first set bit in a bitset and you want to* avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.*/namespace android {// A simple set of 32 bits that can be individually marked or cleared.
struct BitSet32 {uint32_t value;inline BitSet32() : value(0UL) { }explicit inline BitSet32(uint32_t value) : value(value) { }// Gets the value associated with a particular bit index.// 函数作用表示向右移动多少位// 0x80000000UL 32位数表示为 10000000000000000000000000000000static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; }// Clears the bit set.inline void clear() { clear(value); }static inline void clear(uint32_t& value) { value = 0UL; }// Returns the number of marked bits in the set.inline uint32_t count() const { return count(value); }static inline uint32_t count(uint32_t value) {return static_cast<uint32_t>(__builtin_popcountl(value));}// Returns true if the bit set does not contain any marked bits.inline bool isEmpty() const { return isEmpty(value); }static inline bool isEmpty(uint32_t value) { return ! value; }// Returns true if the bit set does not contain any unmarked bits.inline bool isFull() const { return isFull(value); }static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; }// Returns true if the specified bit is marked.inline bool hasBit(uint32_t n) const { return hasBit(value, n); }static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); }// Marks the specified bit.inline void markBit(uint32_t n) { markBit(value, n); }static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); }// Clears the specified bit.// 向右移动多少位在取反,在与运算,等于保留其他位,将特定的位取反inline void clearBit(uint32_t n) { clearBit(value, n); }static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); }// Finds the first marked bit in the set.// Result is undefined if all bits are unmarked.inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }static uint32_t firstMarkedBit(uint32_t value) { return clz_checked(value); }// Finds the first unmarked bit in the set.// Result is undefined if all bits are marked.inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }static inline uint32_t firstUnmarkedBit(uint32_t value) { return clz_checked(~ value); }// Finds the last marked bit in the set.// Result is undefined if all bits are unmarked.inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - ctz_checked(value); }// Finds the first marked bit in the set and clears it.  Returns the bit index.// Result is undefined if all bits are unmarked.// 第一步先获取前导0多少位,则第一个为1前面多少位0// 第二步清除对应位数的1变为0// 返回前面有多少位inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }static inline uint32_t clearFirstMarkedBit(uint32_t& value) {uint32_t n = firstMarkedBit(value);clearBit(value, n);return n;}// Finds the first unmarked bit in the set and marks it.  Returns the bit index.// Result is undefined if all bits are marked.// 1.先获取前导数量为0的个数// 2.对对应的位数标记为1// 3.返回前导数量inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }static inline uint32_t markFirstUnmarkedBit(uint32_t& value) {uint32_t n = firstUnmarkedBit(value);markBit(value, n);return n;}// Finds the last marked bit in the set and clears it.  Returns the bit index.// Result is undefined if all bits are unmarked.inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }static inline uint32_t clearLastMarkedBit(uint32_t& value) {uint32_t n = lastMarkedBit(value);clearBit(value, n);return n;}// Gets the index of the specified bit in the set, which is the number of// marked bits that appear before the specified bit.inline uint32_t getIndexOfBit(uint32_t n) const {return getIndexOfBit(value, n);}static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));}inline bool operator== (const BitSet32& other) const { return value == other.value; }inline bool operator!= (const BitSet32& other) const { return value != other.value; }inline BitSet32 operator& (const BitSet32& other) const {return BitSet32(value & other.value);}inline BitSet32& operator&= (const BitSet32& other) {value &= other.value;return *this;}inline BitSet32 operator| (const BitSet32& other) const {return BitSet32(value | other.value);}inline BitSet32& operator|= (const BitSet32& other) {value |= other.value;return *this;}private:// We use these helpers as the signature of __builtin_c{l,t}z has "unsigned int" for the// input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.static inline uint32_t clz_checked(uint32_t value) {if (sizeof(unsigned int) == sizeof(uint32_t)) {return static_cast<uint32_t>(__builtin_clz(value));} else {return static_cast<uint32_t>(__builtin_clzl(value));}}static inline uint32_t ctz_checked(uint32_t value) {if (sizeof(unsigned int) == sizeof(uint32_t)) {return static_cast<uint32_t>(__builtin_ctz(value));} else {return static_cast<uint32_t>(__builtin_ctzl(value));}}
};ANDROID_BASIC_TYPES_TRAITS(BitSet32)// A simple set of 64 bits that can be individually marked or cleared.
struct BitSet64 {uint64_t value;inline BitSet64() : value(0ULL) { }explicit inline BitSet64(uint64_t value) : value(value) { }// Gets the value associated with a particular bit index.// 右移动几位static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; }// Marks the specified bit.// 对该位赋值为1inline void markBit(uint32_t n) { markBit(value, n); }static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); }// Clears the specified bit.inline void clearBit(uint32_t n) { clearBit(value, n); }static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); }// Finds the first marked bit in the set.// Result is undefined if all bits are unmarked.// 输出前导为0的个数,如 8 32位数// 0000 0000 0000 0000 0000 0000 0000 1000  28个前导0 输出28inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }static inline uint32_t firstMarkedBit(uint64_t value) {return static_cast<uint32_t>(__builtin_clzll(value));}// Finds the first unmarked bit in the set.// Result is undefined if all bits are marked.// 输出前导为0的个数,如 8 32位数// 注意这里取反获取//       1000 0000 0000 0000 0000 0000 0000 0000// 取反得 0111 1111 1111 1111 1111 1111 1111 1111// 返回1inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }static inline uint32_t firstUnmarkedBit(uint64_t value) {return static_cast<uint32_t>(__builtin_clzll(~value));}// Finds the first unmarked bit in the set and marks it.  Returns the bit index.// Result is undefined if all bits are marked.// 1.先获取前导数量为0的个数 里面做了取反// 2.对对应的位数标记为1// 3.返回前导数量inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {uint32_t n = firstUnmarkedBit(value);markBit(value, n);return n;}
};ANDROID_BASIC_TYPES_TRAITS(BitSet64)} // namespace android#endif // UTILS_BITSET_H

总结 输入事件 先将对应的RawEvent 转化为 slot 类,在将 slot类转化为 RawPointerData,并算出触摸对应的id 和index。

数据的加工和分发

事件处理完后, 要到 inputDispatcher 还需要进一步的加工 ,执行完 syncTouch后,调用 processRawTouches。对事件进行一个加工。

void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {// Push a new state.mRawStatesPending.emplace_back();// 获取rawState,并对变量进行重置RawState& next = mRawStatesPending.back();next.clear();next.when = when;next.readTime = readTime;// Sync button state.next.buttonState =mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState();// Sync scrollnext.rawVScroll = mCursorScrollAccumulator.getRelativeVWheel();next.rawHScroll = mCursorScrollAccumulator.getRelativeHWheel();mCursorScrollAccumulator.finishSync();// Sync touch// 调用多指 MultiTouchInputMapper的syncTouchsyncTouch(when, &next);...// 对数据进行加工processRawTouches(false /*timeout*/);
}

查看对应的方法,一开始对 mDeviceMode属性进行判断,如果为 disable 则不处理事件。然后会调用 cookAndDispatch 方法进行事件的处理和分发。

// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp
void TouchInputMapper::processRawTouches(bool timeout) {// 如果不允许触摸就拦截if (mDeviceMode == DeviceMode::DISABLED) {// Drop all input if the device is disabled.cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);mCurrentCookedState.clear();updateTouchSpots();return;}// Drain any pending touch states. The invariant here is that the mCurrentRawState is always// valid and must go through the full cook and dispatch cycle. This ensures that anything// touching the current state will only observe the events that have been dispatched to the// rest of the pipeline.const size_t N = mRawStatesPending.size();size_t count;for (count = 0; count < N; count++) {const RawState& next = mRawStatesPending[count];// A failure to assign the stylus id means that we're waiting on stylus data// and so should defer the rest of the pipeline.if (assignExternalStylusId(next, timeout)) {break;}// All ready to go.clearStylusDataPendingFlags();mCurrentRawState.copyFrom(next);if (mCurrentRawState.when < mLastRawState.when) {mCurrentRawState.when = mLastRawState.when;mCurrentRawState.readTime = mLastRawState.readTime;}// 加工和分发cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);}if (count != 0) {mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);}if (mExternalStylusDataPending) {if (timeout) {nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;clearStylusDataPendingFlags();mCurrentRawState.copyFrom(mLastRawState);
#if DEBUG_STYLUS_FUSIONALOGD("Timeout expired, synthesizing event with new stylus data");
#endifconst nsecs_t readTime = when; // consider this synthetic event to be zero latencycookAndDispatch(when, readTime);} else if (mExternalStylusFusionTimeout == LLONG_MAX) {mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);}}
}

cookAndDispatch 方法 主要对 2步

  1. 调用 cookPointerData 将数据做一个转换,将对应的RawPointerData 加工为 PointerCoords,主要对数据跟设备的配置参数做一个校准,进行相对的转换,转换x ,y 符合屏幕数据
  2. 调用 dispatchTouches, 对事件是 down 还是 move ,up进行判断。
void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {// Always start with a clean state.mCurrentCookedState.clear();// Apply stylus buttons to current raw state.applyExternalStylusButtonState(when);// Handle policy on initial down or hover events.bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&mCurrentRawState.rawPointerData.pointerCount != 0;uint32_t policyFlags = 0;bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;if (initialDown || buttonsPressed) {// If this is a touch screen, hide the pointer on an initial down.if (mDeviceMode == DeviceMode::DIRECT) {getContext()->fadePointer();}if (mParameters.wake) {policyFlags |= POLICY_FLAG_WAKE;}}// Consume raw off-screen touches before cooking pointer data.// If touches are consumed, subsequent code will not receive any pointer data.// 消费屏幕外的输入事件if (consumeRawTouches(when, readTime, policyFlags)) {mCurrentRawState.rawPointerData.clear();}// Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure// with cooked pointer data that has the same ids and indices as the raw data.// The following code can use either the raw or cooked data, as needed.// 将对应的RawPointerData 加工为 PointerCoordscookPointerData();// Apply stylus pressure to current cooked state.applyExternalStylusTouchState(when);// Synthesize key down from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,mCurrentCookedState.buttonState);// Dispatch the touches either directly or by translation through a pointer on screen.if (mDeviceMode == DeviceMode::POINTER) {for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer =mCurrentRawState.rawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentCookedState.stylusIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {mCurrentCookedState.fingerIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {mCurrentCookedState.mouseIdBits.markBit(id);}}for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer =mCurrentRawState.rawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentCookedState.stylusIdBits.markBit(id);}}// Stylus takes precedence over all tools, then mouse, then finger.PointerUsage pointerUsage = mPointerUsage;if (!mCurrentCookedState.stylusIdBits.isEmpty()) {mCurrentCookedState.mouseIdBits.clear();mCurrentCookedState.fingerIdBits.clear();pointerUsage = PointerUsage::STYLUS;} else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {mCurrentCookedState.fingerIdBits.clear();pointerUsage = PointerUsage::MOUSE;} else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||isPointerDown(mCurrentRawState.buttonState)) {pointerUsage = PointerUsage::GESTURES;}dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);} else {updateTouchSpots();if (!mCurrentMotionAborted) {dispatchButtonRelease(when, readTime, policyFlags);dispatchHoverExit(when, readTime, policyFlags);// 对事件进行分发,判断是move 还是down,up事件dispatchTouches(when, readTime, policyFlags);dispatchHoverEnterAndMove(when, readTime, policyFlags);dispatchButtonPress(when, readTime, policyFlags);}if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {mCurrentMotionAborted = false;}}// Synthesize key up from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,mViewport.displayId, policyFlags, mLastCookedState.buttonState,mCurrentCookedState.buttonState);// Clear some transient state.mCurrentRawState.rawVScroll = 0;mCurrentRawState.rawHScroll = 0;// Copy current touch to last touch in preparation for the next cycle.mLastRawState.copyFrom(mCurrentRawState);mLastCookedState.copyFrom(mCurrentCookedState);
}

cookPointerData 方法 ,开始遍历并通过 scale 等参数进行数据的转换,转换后存到 PointerCoords 中。

void TouchInputMapper::cookPointerData() {uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;mCurrentCookedState.cookedPointerData.clear();mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;mCurrentCookedState.cookedPointerData.hoveringIdBits =mCurrentRawState.rawPointerData.hoveringIdBits;mCurrentCookedState.cookedPointerData.touchingIdBits =mCurrentRawState.rawPointerData.touchingIdBits;mCurrentCookedState.cookedPointerData.canceledIdBits =mCurrentRawState.rawPointerData.canceledIdBits;if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {mCurrentCookedState.buttonState = 0;} else {mCurrentCookedState.buttonState = mCurrentRawState.buttonState;}// Walk through the the active pointers and map device coordinates onto// surface coordinates and adjust for display orientation.for (uint32_t i = 0; i < currentPointerCount; i++) {const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];...// Distancefloat distance;switch (mCalibration.distanceCalibration) {case Calibration::DistanceCalibration::SCALED:distance = in.distance * mDistanceScale;break;default:distance = 0;}// Adjust X,Y coords for device calibration// TODO: Adjust coverage coords?// 转化为浮点数float xTransformed = in.x, yTransformed = in.y;mAffineTransform.applyTo(xTransformed, yTransformed);// 获取对应的x,y坐标rotateAndScale(xTransformed, yTransformed);// Adjust X, Y, and coverage coords for surface orientation.float left, top, right, bottom;// 根据屏幕方向算出对应的left rightswitch (mSurfaceOrientation) {case DISPLAY_ORIENTATION_90:left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;orientation -= M_PI_2;if (mOrientedRanges.haveOrientation &&orientation < mOrientedRanges.orientation.min) {orientation +=(mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;case DISPLAY_ORIENTATION_180:left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;orientation -= M_PI;if (mOrientedRanges.haveOrientation &&orientation < mOrientedRanges.orientation.min) {orientation +=(mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;case DISPLAY_ORIENTATION_270:left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;orientation += M_PI_2;if (mOrientedRanges.haveOrientation &&orientation > mOrientedRanges.orientation.max) {orientation -=(mOrientedRanges.orientation.max - mOrientedRanges.orientation.min);}break;default:left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;break;}// Write output coords.// 将值写进 PointerCoordsPointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];out.clear();out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed);out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed);out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor);out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt);out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);if (mCalibration.coverageCalibration == Calibration::CoverageCalibration::BOX) {out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right);out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom);} else {out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);}...// Write id index and mark id as valid.// 对应的index和idmCurrentCookedState.cookedPointerData.idToIndex[id] = i;mCurrentCookedState.cookedPointerData.validIdBits.markBit(id);}
}

参数的读取 可以按以下方法。
执行 getevent -ltr 然后触摸获取对应的 触摸点 如获取到的为 /dev/input/event7
然后在执行 dumpsys input 获取对应的信息,然后看对应的名称,如下图 /dev/input/event7 对应的是 synaptics_tcm_touch
在这里插入图片描述
在通过查看对应的设备7 则可以看到相关的信息
在这里插入图片描述

dispatchTouches 方法中,则通过上一个事件的bit和当前的bit位,比较获取判断事件类型。
如果前后int值相等,则肯定为 move事件
在通过与或判断是否有按下或者抬起事件,需要注意 move事件判断是否

void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {// 获取当前的触点BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;// 上一次的触点BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;int32_t metaState = getContext()->getGlobalMetaState();int32_t buttonState = mCurrentCookedState.buttonState;// 前后一样,肯定是move事件if (currentIdBits == lastIdBits) {if (!currentIdBits.isEmpty()) {// No pointer id changes so this is a move event.// The listener takes care of batching moves so we don't have to deal with that here.dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}} else {// There may be pointers going up and pointers going down and pointers moving// all at the same time.// 取出抬起的BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);// 取出按下的BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);// 取出移动的BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);BitSet32 dispatchedIdBits(lastIdBits.value);// Update last coordinates of pointers that have moved so that we observe the new// pointer positions at the same time as other pointers that have just gone up.// 判断移动的bits是否坐标一样,不一样需要移动bool moveNeeded =updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex,mLastCookedState.cookedPointerData.pointerProperties,mLastCookedState.cookedPointerData.pointerCoords,mLastCookedState.cookedPointerData.idToIndex, moveIdBits);if (buttonState != mLastCookedState.buttonState) {moveNeeded = true;}// Dispatch pointer up events.// 遍历抬起的id,传递up事件while (!upIdBits.isEmpty()) {uint32_t upId = upIdBits.clearFirstMarkedBit();bool isCanceled = mCurrentCookedState.cookedPointerData.canceledIdBits.hasBit(upId);if (isCanceled) {ALOGI("Canceling pointer %d for the palm event was detected.", upId);}dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,mLastCookedState.cookedPointerData.pointerProperties,mLastCookedState.cookedPointerData.pointerCoords,mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,mOrientedXPrecision, mOrientedYPrecision, mDownTime);dispatchedIdBits.clearBit(upId);mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId);}// Dispatch move events if any of the remaining pointers moved from their old locations.// Although applications receive new locations as part of individual pointer up// events, they do not generally handle them except when presented in a move event.// 判断是否移动事件if (moveNeeded && !moveIdBits.isEmpty()) {ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,metaState, buttonState, 0,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}// Dispatch pointer down events using the new pointer locations.// 如果down不为空,遍历下while (!downIdBits.isEmpty()) {uint32_t downId = downIdBits.clearFirstMarkedBit();dispatchedIdBits.markBit(downId);if (dispatchedIdBits.count() == 1) {// First pointer is going down.  Set down time.mDownTime = when;}dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,0, 0, metaState, buttonState, 0,mCurrentCookedState.cookedPointerData.pointerProperties,mCurrentCookedState.cookedPointerData.pointerCoords,mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/52508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的jieba库中文分词词频统计和合并

可能在设置问题模板的时候需要分析已有问句&#xff0c;然后统计词频&#xff0c;根据词频设计问题模板

31套科技风PPT免费分享

目录 部分展示 部分展示 #PPT下载 「科技风模板」链接&#xff1a;https://pan.quark.cn/s/fb2f39a1d343 链接永久有效&#xff0c;点击这里下载&#xff0c;记得给个赞哦

Java生成一个5位的随机验证码(大小写字母和数字)

生成验证码 内容&#xff1a;可以是小写字母&#xff0c;也可以是大写字母&#xff0c;还可以是数字 规则&#xff1a;长度为5 内容中四位字母&#xff0c;一位数字 其中数字只有一位&#xff0c;但是可以出现在任意位置。 package test;impo…

QT error: expected ‘:‘ before ‘slots‘ public slots:

C:\Users\Administrator\Desktop\VideoHill\GikISearch\net.h:10: error: expected : before slots public slots: 先看看头文件里有没有加上引用包含#include <xxxx> 也就是一个引用包含都没有 没有就会报这个。至少一个。 加上后

【架构-24】XML和JSON

XML&#xff08;可扩展标记语言&#xff09;和JSON&#xff08;JavaScript对象表示法&#xff09;是两种常用的数据格式&#xff0c;用于在不同系统之间传输和交换数据。它们各有优点和缺点&#xff0c;适用于不同的场景。下面是对XML和JSON的简要介绍以及它们之间的对比。 XM…

Chapter 01 Vue入门

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 前言一、Vue简介二、el:挂载点三、data&#xff1a;数据对象四、第一个Vue程序五、安装 Vue 开发者工具 前言 Vue 是一个框架&#xff0c;也是一个生态&#xff0c;其功能覆盖…

Modbus-TCP——Libmodbus安装和使用(Ubuntu22.04)

1、简介 Modbus是一种通信协议&#xff0c;广泛用于工业自动化和过程控制领域&#xff0c;允许不同设备之间进行数据交换。libmodbus是一个用于 Modbus 协议的开源库&#xff0c;主要用于开发和实现 Modbus 协议的客户端和服务器应用程序。libmodbus 以 C 语言编写&#xff0c…

【云原生】MySQL的源码编译

1、实验环境 &#xff08;1&#xff09;虚拟机版本&#xff1a;RHEL7.9 &#xff08;2&#xff09;主机 主机名称IP地址mysql-node1172.25.254.10mysql-node2172.25.254.20 2、实验步骤 注意&#xff1a;我们的两台主机都要进行MySQL源码编译&#xff0c;并且操作相同&…

探索 InternLM 模型能力边界

一、任务介绍 在 CompassArena 中选择双模型对话&#xff0c;与InternLM2.5及另外任意其他模型对话&#xff0c;收集 5 个 InternLM2.5 输出结果不如其他模型的对话案例&#xff0c;以及 InternLM2.5 的 5 个 Good Case。 任务地址&#xff1a;Docs Bad Case 1&#xff1a; 模…

Transforms的学习以及地址问题

一、地址问题 在学习Dataset类的实战与Tensboard的学习中&#xff0c;有出现一些地址的问题&#xff1a; 1、相对地址 相对地址的使用&#xff1a; 使用于在从端口中&#xff0c;打开TensorBoard的页面。使用的就是相对地址&#xff1b;例如&#xff1a; tensorboard --log…

新书推荐:《分布式商业生态战略:数字商业新逻辑与企业数字化转型新策略》

近两年&#xff0c;商业经济环境的不确定性越来越明显&#xff0c;市场经济受到疫情、技术、政策等多方因素影响越来越难以预测&#xff0c;黑天鹅事件时有发生。在国内外经济方面&#xff0c;国际的地缘政治对商业经济产生着重大的影响&#xff0c;例如供应链中断&#xff0c;…

rabbitMQ安装与简单demo

安装 mac安装有了brew很方便&#xff0c;windows的可参考 win10 安装rabbitMQ详细步骤 brew install rabbitmq启动 brew services start rabbitmq关闭 brew services stop rabbitmq出了问题之后可以重启一下 brew services restart rabbitmqsome issue 某些库下载超时 比…

使用vagrant、virtualbox、快速创建kali linux

使用vagrant、virtualbox、快速创建kali linux 初始化kali下载vagrant相应镜像vagrant添加相应镜像创建vagrantfile在vagrantfile根目录执行cmd虚拟机登录密码修改sshd配置 用shell远程链接(可选)可视化界面设置成中文创建成功展示图 添加实体网卡使用kali 破解WiFi密码解决 on…

Godot《躲避小兵》实战之为游戏添加音效

现在&#xff0c;我们已经完成了游戏的所有功能。以下是一些剩余的步骤&#xff0c;为游戏加点“料”&#xff0c;改善游戏体验。 随意用你自己的想法扩展游戏玩法。 背景 默认的灰色背景不是很吸引人&#xff0c;那么我们就来改一下颜色。一种方法是使用 ColorRect节点。将…

Ubuntu技巧-Ubuntu远程访问之电信公网IP

&#x1f4a1; 大家好&#xff0c;我是可夫小子&#xff0c;《小白玩转ChatGPT》专栏作者&#xff0c;关注AIGC、互联网和自媒体。 前面文章介绍了家庭服务器接入外网的三种方式的第一种&#xff0c;今天介绍第二种&#xff0c;即通过获得电脑公网IP&#xff0c;然后再设置动态…

QT Quick QML 网络助手——TCP客户端

GitHub 源码: QmlLearningPro &#xff0c;选择子工程 Nettools.pro QML 其它文章请点击这里: QT QUICK QML 学习笔记 ● 运行效果&#xff1a; 左侧为常用的网络调试工具&#xff0c;右侧为本项目 UI 效果&#xff0c;前端使用 QML &#xff0c;后端使用C &#xff…

linux(Ubuntu )搭C++ 最新版GDAL完整教程

在前面的文章中主要是介绍如何在windows系统下利用python安装gdal库&#xff0c;如下&#xff1a; 如何快速安装GDAL 在linux环境下python安装gdal也可以利用现成的whl文件&#xff0c;但是安装c GDAL环境的比较麻烦&#xff0c;目前网络上大多是安装的老版本的教程&#xff…

自适应学习率(Datawhale X 李宏毅苹果书 AI夏令营)

传统的梯度下降方法在优化过程中常常面临学习率设置不当的问题。固定的学习率在训练初期可能过大&#xff0c;导致模型训练不稳定&#xff0c;而在后期可能过小&#xff0c;导致训练速度缓慢。为了克服这些问题&#xff0c;自适应学习率方法应运而生。这些方法通过动态调整学习…

ssrf漏洞之——漏洞复现

漏洞介绍 SSRF漏洞&#xff1a;SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由恶意访问者构造url&#xff0c;由服务端对此url发起请求的一个安全漏洞。 漏洞原理 SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能&#xff0c;并且没有对目…

工业4G路由器

设备概述 路由器是基于4G 技术研发的无线路由网关设备&#xff0c;除了具备传统路由器 的 VPN 、防火墙、 NAT 、 PPPoE 、 DHCP 等功能之外&#xff0c;还能支持 4G 无线拨号&#xff0c;提供最高可达 150Mbps 的无线高速带宽。路由器支持四个以太网接口&#xff0c;可更好…