Android 输入系统

news/2024/5/5 12:43:33/文章来源:https://blog.csdn.net/tianzhaoai/article/details/130532742

概述

Android 输入系统的工作原理概括来说,内核将原始事件写入到设备节点中,InputReader 不断地通过 EventHub 将原始事件取出来并翻译加工成 Android 输入事件,然后交给 InputDispatcher。

InputDispatcher 根据 WMS 提供的窗口信息将事件交给合适的窗口。窗口的 ViewRootImpl 对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的画面、执行特定的动作
在这里插入图片描述

概念解释

InputManagerService(IMS):Android 系统服务,分为 Java 层和 Native 层两部分,Java 层负责与 WMS 通信,而 Native 层则是 InputReader 和 InputDispatcher 两个输入系统关键组件的运行容器;
WindowManagerService(WMS):它并不是输入系统的一员,新建窗口时,WMS 为新窗口和 IMS 创建了事件传递所用的通道,会将窗口的可点击区域,焦点窗口等信息实时更新到 IMS 的 InputDispatcher 中,使得 InputDispatcher 可以正确将事件派发到指定窗口;
EventHub:直接访问所有的设备节点,通过 getEvents() 函数将所有输入系统相关的待处理的底层事件返回给使用者,包括原始输入事件,设备节点的增删等;
InputReader:运行在一个独立的线程中,负责管理输入设备的列表和配置,以及进行输入事件的加工处理,它通过其线程循环不断地通过 getEvents() 函数从 EventHub 中将事件取出并进行处理,对于设备节点的增删事件,它会更新输入设备列表与配置;对于原始输入事件,InputReader对其进行翻译,组装,封装为包含更多信息,更多可读性的输入事件,然后交给InputDispatcher进行派发;
InputDispatcher:运行于一个独立的线程中,InputDispatcher 中保管来自 WMS 的所有窗口的信息,收到 InputReader 的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口;
ViewRootImpl:对某些窗口,如 SurfaceView 的窗口来说,窗口就是输入事件派发的终点,而对其他的如Activity,对话框等使用了 Android 控件系统的窗口来说,输入事件的终点是控件;
在这里插入图片描述

Input 系统流程

服务端

Input 系统初始化 IMS

SystemServer中 的 ServerThread 线程中启动 InputManagerService

  1. 创建 IMS 对象
    1. 创建 NativeInputManager 对象,此对象将是 Native 层组件与 Java 层 IMS 进行通信的桥梁
    2. 创建 Native 层的 InputManager,创建 InputDispatcher、InputReader、EventHub
  2. 将 IMS 添加到服务中心
  3. 调用 IMS 对象的 start() 函数完成 InputDispatcherThread 、InputReaderThread 的创建和启动
    1. InputReader 在其线程循环中不断地从 EventHub 中抽取原始输入事件,进行加工处理后将加工所得的事件放入 InputDispatcher 的派发发队列中
    2. InputDispatcher 则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中
    3. 窗口事件接收线程的 Looper 从管道中将事件取出,交由事件处理函数进行事件响应
      暂时无法在飞书文档外展示此内容

源码分析

SystemServer.java {public static void main(String[] args) {new SystemServer().run();}@Overridepublic void run() {startOtherServices(t);}private void startOtherServices(@NonNull TimingsTraceAndSlog t) {InputManagerService inputManager = null;inputManager = new InputManagerService(context);// 将IMS发布给ServiceManager,以便其他人可以访问IMS提供的接口ServiceManager.addService(Context.INPUT_SERVICE,inputManager);// 设置向WMS发起回调的callback对象inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());// 正式启动IMSinputManager.start();}
}
InputManagerService.java {static class Injector {private final Context mContext;private final Looper mLooper;NativeInputManagerService getNativeService(InputManagerService service) {return new NativeInputManagerService.NativeImpl(service, mContext, mLooper.getQueue());}}InputManagerService(Injector injector) {mHandler = new InputManagerHandler(injector.getLooper());mNative = injector.getNativeService(this);}public void start() {Slog.i(TAG, "Starting input manager");mNative.start();}
}
NativeInputManagerService.java {class NativeImpl implements NativeInputManagerService {/** Pointer to native input manager service object, used by native code. */private final long mPtr;NativeImpl(InputManagerService service, Context context, MessageQueue messageQueue) {mPtr = init(service, context, messageQueue);}private native long init(InputManagerService service, Context context,MessageQueue messageQueue);public native void start();}
}InputManagerService.cpp {static const JNINativeMethod gInputManagerMethods[] = {{"init","(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",(void*)nativeInit},{"start", "()V", (void*)nativeStart},}static jlong nativeInit(JNIEnv* env, jclass /* clazz */,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);// 新建了一个 NativeInputManager 对象// 此对象将是 Native 层组件与 Java 层 IMS 进行通信的桥梁// 实现了 InputReaderPolicyInterface 与 InputDispatcherPolicyInterface 两个接口NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());return reinterpret_cast<jlong>(im);}static void nativeStart(JNIEnv* env, jobject nativeImplObj) {NativeInputManager* im = getNativeInputManager(env, nativeImplObj);status_t result = im->getInputManager()->start();}static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {return reinterpret_cast<NativeInputManager*>(env->GetLongField(clazz,gNativeInputManagerServiceImpl.mPtr));}
}
NativeInputManager.cpp {NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :mLooper(looper), mInteractive(true) {// 创建 Native 层的 InputManagerInputManager* im = new InputManager(this, this);mInputManager = im;defaultServiceManager()->addService(String16("inputflinger"), im);}
}
InputManager.cpp {InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {// 创建 InputDispatchermDispatcher = createInputDispatcher(dispatcherPolicy);//这里的 mBlocker 即封装的 mDispatcher ,mDispatcher 实现了 InputListenerInterface 接口//通过 getListener().notifyMotion(&args) 通知 mDispatcher 处理事件mClassifier = std::make_unique<InputClassifier>(*mDispatcher);mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier);// 创建 InputReadermReader = createInputReader(readerPolicy, *mBlocker);}status_t InputManager::start() {status_t result = mDispatcher->start();result = mReader->start();return OK;}
}
InputReaderFactory.cpp {std::unique_ptr<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {// 这里会创建 EventHub ,并设置事件回调return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);}
}
InputDispatcherFactory.cpp {std::unique_ptr<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);}
}
InputDispatcher.cpp {status_t InputDispatcher::start() {// 创建供 InputDispatcher 运行的线程 InputDispatcherThreadmThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;}
}
InputReader.cpp {status_t InputReader::start() {// 创建供 InputReader 运行的线程 InputReaderThreadmThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;}
}

事件读取(EventHub)与加工(InputReader)

EventHub

  • 避免不断地轮询这些描述符
  • 不需要为每个描述符创建独立的线程进行阻塞读取,避免了资源浪费的同时又可以获得较快的响应速度。

INotify:Linux 内核所提供的一种文件系统变化通知机制。可以监控文件系统的变化,如文件新建、删除、读写等

  • inotify_init() 创建一个 inotify 对象,返回一个 notifyFd。
  • inotify_add_watch 将一个或多个监听添加到 inotify 对象中。
  • read() 函数从 inotify 对象中读取监听事件

Epoll:监听多个描述符的可读/可写状态。等待返回时携带了可读的描述符

  • epoll_create(int max_fds) 创建一个 epoll 对象的描述符,之后对 epoll 的操作均使用这个描述符完成。max_fds 参数表示了此 epoll 对象可以监听的描述符的最大数量。
  • epoll_ctl (int epfd, int op,int fd, struct epoll_event *event) 用于管理注册事件的函数。这个函数可以增加/删除/修改事件的注册。
  • int epoll_wait(int epfd, structepoll_event * events, int maxevents, int timeout) 用于等待事件的到来。当此函数返回时,events 数组参数中将会包含产生事件的文件描述符。
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) {//使用epoll_create()函数创建一个epoll对象,用来监听设备节点是否有数据可读(有无事件)mEpollFd = epoll_create1(EPOLL_CLOEXEC);//创建一个 inotify 对象 。这个对象将被用来 监听设备节点的增删事件mINotifyFd = inotify_init1(IN_CLOEXEC);std::error_code errorCode;bool isDeviceInotifyAdded = false;if (std::filesystem::exists("/dev/input", errorCode)) {// 将存储设备节点的路径 /dev/input 作为监听对象添加到inotify对象中// 当此文件夹下的设备节点发生创建与删除事件时,都可以通过mINotifyFd读取事件的详细信息mDeviceInputWd = inotify_add_watch(mINotifyFd, "/dev/input", IN_DELETE | IN_CREATE);}struct epoll_event eventItem = {};eventItem.events = EPOLLIN | EPOLLWAKEUP;eventItem.data.fd = mINotifyFd;// 将 mINotifyFd 作为 epoll 的一个监控对象// 当 inotify 事件到来时,epoll_wait() 将立刻返回// EventHub 便可从 mINotifyFd 中读取设备节点的增删信息,并作相应处理int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);}
}

InputReader

在这里插入图片描述

源码分析

InputReader.cpp {status_t InputReader::start() {mThread = std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });return OK;}void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;std::vector<InputDeviceInfo> inputDevices;// 读取事件,没事件时会 blocksize_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockif (count) {processEventsLocked(mEventBuffer, count);}} // release lockmQueuedListener.flush();}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;// 处理 输入 事件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 happenbreak;}}}}void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,size_t count) {auto deviceIt = mDevices.find(eventHubId);std::shared_ptr<InputDevice>& device = deviceIt->second;device->process(rawEvents, count);}
}
EventHub.cpp {size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {struct input_event readBuffer[bufferSize];RawEvent* event = buffer;Device* device = getDeviceByFdLocked(eventItem.data.fd);while (mPendingEventIndex < mPendingEventCount) {// mPendingEventIndex 指定尚未处理的 epoll_event 的索引const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];Device* device = getDeviceByFdLocked(eventItem.data.fd);for (;;) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);// This must be an input eventif (eventItem.events & EPOLLIN) {// 根据设备id 读取事件到 readBuffer 中int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity);int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;size_t count = size_t(readSize) / sizeof(struct input_event);for (size_t i = 0; i < count; i++) {struct input_event& iev = readBuffer[i];event->when = processEventTimestamp(iev);event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);event->deviceId = deviceId;event->type = iev.type;event->code = iev.code;event->value = iev.value;event += 1;capacity -= 1;}break;} }// 执行 epoll_wait() 函数等待新的事件到来int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);}// All done, return the number of events we read.return event - buffer;}
}
InputDevice.cpp {void InputDevice::process(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {mapper.process(rawEvent);});--count;}}
}
SingleTouchInputMapper.cpp {void SingleTouchInputMapper::process(const RawEvent* rawEvent) {TouchInputMapper::process(rawEvent);mSingleTouchMotionAccumulator.process(rawEvent);}
}
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);}}void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {// Push a new state.mRawStatesPending.emplace_back();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();mCursorScrollAccumulator.finishSync();processRawTouches(false /*timeout*/);}void TouchInputMapper::processRawTouches(bool timeout) {cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);}void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {updateTouchSpots();// 内部处理各个事件,不需要的会进行拦截dispatchButtonRelease(when, readTime, policyFlags);dispatchHoverExit(when, readTime, policyFlags);dispatchTouches(when, readTime, policyFlags);dispatchHoverEnterAndMove(when, readTime, policyFlags);dispatchButtonPress(when, readTime, policyFlags);}void TouchInputMapper::dispatch***(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {dispatchMotion(...);}void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,uint32_t source, int32_t action, int32_t actionButton,int32_t flags, int32_t metaState, int32_t buttonState,int32_t edgeFlags, const PointerProperties* properties,const PointerCoords* coords, const uint32_t* idToIndex,BitSet32 idBits, int32_t changedId, float xPrecision,float yPrecision, nsecs_t downTime) {NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,policyFlags, action, actionButton, flags, metaState, buttonState,MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,downTime, std::move(frames));// Listener 为 InputDispatcher 实现getListener().notifyMotion(&args);}
}
InputDispatcher.cpp {void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {bool needWake = false;{// Just enqueue a new motion event.std::unique_ptr<MotionEntry> newEntry = std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,args->source, args->displayId, policyFlags,args->action, args->actionButton, args->flags,args->metaState, args->buttonState,args->classification, args->edgeFlags,args->xPrecision, args->yPrecision,args->xCursorPosition, args->yCursorPosition,args->downTime, args->pointerCount,args->pointerProperties, args->pointerCoords);needWake = enqueueInboundEventLocked(std::move(newEntry));} // release lockif (needWake) {// 唤醒 InputDispatcher 处理接收到的事件mLooper->wake();}}bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {bool needWake = mInboundQueue.empty();mInboundQueue.push_back(std::move(newEntry));EventEntry& entry = *(mInboundQueue.back());switch (entry.type) {case EventEntry::Type::KEY: {if (...) {needWake = true;}break;}case EventEntry::Type::MOTION: {if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {......needWake = true;}break;}}return needWake;}
}

事件的派发

在这里插入图片描述

源码分析

InputDispatcher.cpp {status_t InputDispatcher::start() {mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;}void InputDispatcher::dispatchOnce() {{ // acquire lock// 处理接收的消息dispatchOnceInnerLocked(&nextWakeupTime);// input anr 检测const nsecs_t nextAnrCheck = processAnrsLocked();} // release lock// 开启下次轮训等待 weak 唤醒mLooper->pollOnce(timeoutMillis);}void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {// 取出从 reader 线程入队的事件mPendingEvent = mInboundQueue.front();mInboundQueue.pop_front();switch (mPendingEvent->type) {case EventEntry::Type::KEY: {std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);break;}case EventEntry::Type::MOTION: {std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent);done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);break;}}}bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {// Identify targets.std::vector<InputTarget> inputTargets;addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));dispatchEventLocked(currentTime, entry, inputTargets);return true;}void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,std::shared_ptr<EventEntry> eventEntry,const std::vector<InputTarget>& inputTargets) {for (const InputTarget& inputTarget : inputTargets) {// Connection 对象中,有两个集合,分别是 outboundQueue 和 waitQueue。// outboundQueue 集合中存放发送给 APP 侧的输入事件// waitQueue 中存放发送给 APP 侧但还没收到回应的输入事件。sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel->getConnectionToken());if (connection != nullptr) {prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);}}}void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);}void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection,std::shared_ptr<EventEntry> eventEntry,const InputTarget& inputTarget) {bool wasEmpty = connection->outboundQueue.empty();// 事件加入到Connection对象中outboundQueue集合enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_***);if (wasEmpty && !connection->outboundQueue.empty()) {startDispatchCycleLocked(currentTime, connection);}}void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {// 取出队首事件,进行分发DispatchEntry* dispatchEntry = connection->outboundQueue.front();// Publish the event.status_t status;const EventEntry& eventEntry = *(dispatchEntry->eventEntry);switch (eventEntry.type) {case EventEntry::Type::KEY: {const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);// Publish the key event.status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId, keyEntry.deviceId,keyEntry.source, keyEntry.displayId,std::move(hmac), dispatchEntry->resolvedAction,dispatchEntry->resolvedFlags, keyEntry.keyCode,keyEntry.scanCode, keyEntry.metaState,keyEntry.repeatCount, keyEntry.downTime,keyEntry.eventTime);break;}case EventEntry::Type::MOTION: {const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);// Publish the motion event.status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId,motionEntry.deviceId, motionEntry.source,motionEntry.displayId, std::move(hmac),dispatchEntry->resolvedAction,motionEntry.actionButton,dispatchEntry->resolvedFlags,motionEntry.edgeFlags, motionEntry.metaState,motionEntry.buttonState,motionEntry.classification,dispatchEntry->transform,motionEntry.xPrecision, motionEntry.yPrecision,motionEntry.xCursorPosition,motionEntry.yCursorPosition,dispatchEntry->rawTransform,motionEntry.downTime, motionEntry.eventTime,motionEntry.pointerCount,motionEntry.pointerProperties, usingCoords);break;}}}
}
InputTransport.cpp {status_t InputPublisher::publishMotionEvent(...) {InputMessage msg;msg.header.type = InputMessage::Type::MOTION;msg.header.seq = seq;msg.body.motion.eventId = eventId;msg.body.motion.deviceId = deviceId;msg.body.motion.source = source;msg.body.motion.displayId = displayId;msg.body.motion.hmac = std::move(hmac);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;......return mChannel->sendMessage(&msg);}status_t InputChannel::sendMessage(const InputMessage* msg) {const size_t msgLength = msg->size();InputMessage cleanMsg;msg->getSanitizedCopy(&cleanMsg);ssize_t nWrite;do {// socket 的方式发送到客户端,通过的是 FD 管道nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);return OK;}
}

客户端

事件接收初始化

在这里插入图片描述

源码分析

ViewRootImpl.java {public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {InputChannel inputChannel = null;if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {inputChannel = new InputChannel();}// 创建 channelres = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mDisplayCutout, inputChannel,mTempInsets, mTempControls);// 通过 loop 监听 channel 的 fdmInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());}
}
Session.java {public int addToDisplayAsUser(...,InputChannel outInputChannel,...) {return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,requestedVisibilities, outInputChannel, outInsetsState, outActiveControls,outAttachedFrame, outSizeCompatScale);}
}
WindowManagerService.java {public int addWindow(...,InputChannel outInputChannel,...){final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}}
}
WindowState.java {void openInputChannel(InputChannel outInputChannel) {String name = getName();// 创建两个 channelmInputChannel = mWmService.mInputManager.createInputChannel(name);mInputChannelToken = mInputChannel.getToken();mInputWindowHandle.setToken(mInputChannelToken);mWmService.mInputToWindowMap.put(mInputChannelToken, this);if (outInputChannel != null) {// 将服务端创建的 InputChannel 赋值给 outInputChannelmInputChannel.copyTo(outInputChannel);}}
}
InputManagerService.java {public InputChannel createInputChannel(String name) {return mNative.createInputChannel(name);}
}
com_android_server_input_InputManagerService.cpp {static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) {NativeInputManager* im = getNativeInputManager(env, nativeImplObj);// Native 层 InputChannel 的创建base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(name);//Java 层 InputChannel 的创建jobject inputChannelObj = android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));return inputChannelObj;}
}
NativeInputManager.cpp {base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(const std::string& name) {return mInputManager->getDispatcher().createInputChannel(name);}
}InputDispatcher.cpp {Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {std::unique_ptr<InputChannel> serverChannel;std::unique_ptr<InputChannel> clientChannel;// 创建 客户端与服务端的 InputChannel,使用 socket 进行通信status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);{ // acquire lockconst sp<IBinder>& token = serverChannel->getConnectionToken();// 封装 connection 用于 服务端事件分发sp<Connection> connection = new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);mConnectionsByToken.emplace(token, connection);std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,this, std::placeholders::_1, token);int fd = serverChannel->getFd();// 服务端 接收 客户端发送的事件,代表 客户端 消息处理完成mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);} // release lockreturn clientChannel;}
}
InputTransport.cpp {status_t InputChannel::openInputChannelPair(const std::string& name,std::unique_ptr<InputChannel>& outServerChannel,std::unique_ptr<InputChannel>& outClientChannel) {int sockets[2];int bufferSize = SOCKET_BUFFER_SIZE;setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));sp<IBinder> token = new BBinder();std::string serverChannelName = name + " (server)";android::base::unique_fd serverFd(sockets[0]);outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);std::string clientChannelName = name + " (client)";android::base::unique_fd clientFd(sockets[1]);outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);return OK;}
}WindowInputEventReceiver.java {public InputEventReceiver(InputChannel inputChannel, Looper looper) {mInputChannel = inputChannel;mMessageQueue = looper.getQueue();mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),mInputChannel, mMessageQueue);}
}
android_view_InputEventReceiver.cpp {static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,jobject inputChannelObj, jobject messageQueueObj) {//根据 java 层 InputChannel 找到对应 Native 层 InputChannelstd::shared_ptr<InputChannel> inputChannel =android_view_InputChannel_getInputChannel(env, inputChannelObj);//根据 java 层 MessageQueue 找到对应 Native 层消息队列sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);//创建 Native 层接收器,inputChannel 交给 mInputConsumer(inputChannel)sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,receiverWeak, inputChannel, messageQueue);status_t status = receiver->initialize();return reinterpret_cast<jlong>(receiver.get());}status_t NativeInputEventReceiver::initialize() {setFdEvents(ALOOPER_EVENT_INPUT);return OK;}void NativeInputEventReceiver::setFdEvents(int events) {if (mFdEvents != events) {mFdEvents = events;int fd = mInputConsumer.getChannel()->getFd();if (events) {//当监听的句柄 fd 发生事件,触发 NativeInputEventReceiver#handleEvent 方法mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);} else {mMessageQueue->getLooper()->removeFd(fd);}}}
}

事件接收

在这里插入图片描述

源码分析

android_view_InputEventReceiver.cpp {//当监听的句柄 fd 发生事件,触发 NativeInputEventReceiver#handleEvent 方法int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);}status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {for (;;) {InputEvent* inputEvent;status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);jobject inputEventObj;switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:inputEventObj = android_view_KeyEvent_fromNative(env,static_cast<KeyEvent*>(inputEvent));break;case AINPUT_EVENT_TYPE_MOTION: {MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);break;}}if (inputEventObj) {// receiverWeak 是 ViewRootImpl 中创建的 WindowInputEventReceiver 对象// call WindowInputEventReceiver 的 dispatchInputEvent 方法env->CallVoidMethod(receiverObj.get(),gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);}}}
}InputTransport.cpp {status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {while (!*outEvent) {// 从 socket 中读取 InputMessagestatus_t result = mChannel->receiveMessage(&mMsg);switch (mMsg.header.type) {case InputMessage::Type::KEY: {KeyEvent* keyEvent = factory->createKeyEvent();// 将 InputMessage 封装成 keyEventinitializeKeyEvent(keyEvent, &mMsg);*outSeq = mMsg.header.seq;*outEvent = keyEvent;break;}case InputMessage::Type::MOTION: {MotionEvent* motionEvent = factory->createMotionEvent();updateTouchState(mMsg);// 将 InputMessage 封装成 MotionEventinitializeMotionEvent(motionEvent, &mMsg);*outSeq = mMsg.header.seq;*outEvent = motionEvent;break;}}return OK;}void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,msg->body.motion.actionButton, msg->body.motion.flags,msg->body.motion.edgeFlags, msg->body.motion.metaState,msg->body.motion.buttonState, msg->body.motion.classification, transform,msg->body.motion.xPrecision, msg->body.motion.yPrecision,msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,pointerCount, pointerProperties, pointerCoords);}status_t InputChannel::receiveMessage(InputMessage* msg) {ssize_t nRead;do {// 通过 socket 接收 InputDispatcher 发送的数据nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);} while (nRead == -1 && errno == EINTR);return OK;}
}

事件分发

在这里插入图片描述

源码分析

ViewRootImpl.java {public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {if (inputChannel != null) {mInputEventReceiver = new WindowInputEventReceiver(inputChannel,Looper.myLooper());}}final class WindowInputEventReceiver extends InputEventReceiver {// call by nativeprivate void dispatchInputEvent(int seq, InputEvent event) {onInputEvent(event);}@Overridepublic void onInputEvent(InputEvent event) {enqueueInputEvent(event, this, 0, true);}}void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);mPendingInputEventTail = q;doProcessInputEvents();}void doProcessInputEvents() {// Deliver all pending input events in the queue.while (mPendingInputEventHead != null) {QueuedInputEvent q = mPendingInputEventHead;deliverInputEvent(q);}}private void deliverInputEvent(QueuedInputEvent q) {try {InputStage stage;stage.deliver(q);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}abstract class InputStage {public final void deliver(QueuedInputEvent q) {result = onProcess(q);}}final class ViewPostImeInputStage extends InputStage {protected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q);} else {return processPointerEvent(q);}}private int processPointerEvent(QueuedInputEvent q) {final MotionEvent event = (MotionEvent)q.mEvent;boolean handled = mView.dispatchPointerEvent(event);return handled ? FINISH_HANDLED : FORWARD;}}
}
View.java {public final boolean dispatchPointerEvent(MotionEvent event) {if (event.isTouchEvent()) {return dispatchTouchEvent(event);} else {return dispatchGenericMotionEvent(event);}}
}
DecorView.java {public boolean dispatchTouchEvent(MotionEvent event) {//Activity 实现了 Window.Callback 这个接口final Window.Callback cb = mWindow.getCallback();return cb != null && !mWindow.isDestroyed() && mFeatureId < 0? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);}
}
Activity.java {public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {onUserInteraction();}if (getWindow().superDispatchTouchEvent(ev)) {return true;}return onTouchEvent(ev);}
}
PhoneWindow.java {public boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);}
}
DecorView.java {public boolean superDispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event);}
}
ViewGroup.java {public boolean dispatchTouchEvent(MotionEvent ev) {onInterceptTouchEvent(ev);for (int i = childrenCount - 1; i >= 0; i--) {dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)}}private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {return child.dispatchTouchEvent(event);}
}
View.java{public boolean dispatchTouchEvent(MotionEvent event) {onTouchEvent(event)}public boolean onTouchEvent(MotionEvent event) {//do sameing}
}

相关工具

getevent

adb shell getevent [-选项] [device_path]
查询 所有设备节点,event0:音量加、电源,event1:音量减
在这里插入图片描述

音量加减、 电源键 事件输入,SYN_REPORT 为单个事件的结束标志。
在这里插入图片描述

常见 EventCode

key描述
EV_KEY键盘、按钮或其他类似按键的状态变化
EV_REL相对坐标轴值的变化,例如鼠标移动5个单位
EV_ABS绝对轴值的变化,例如触摸屏上触摸的坐标
SYN_REPORT将事件同步并打包

<事件发生时间> <事件类型> <事件代码> <事件值>
产生事件时的时间戳([ 2038.027687])
产生事件的设备节点(/dev/input/event0)
事件类型(0001),事件代码(0073)以及事件的值(00000001)
在这里插入图片描述

sendevent

sendevent <节点路径> <类型><代码> <值>
sendevent 的输入参数与 getevent 的输出是对应的,只不过 sendevent 的参数为十进制。
模拟音量+键:音量+ 键的代码 0x73 的十进制为 115
在这里插入图片描述

dumpsys input

$adb shell dumpsys input
Event Hub State:......10: virtual_input_deviceClasses: 0x00000065Path: /dev/input/event9Enabled: trueDescriptor: ea011df68d5e458fae3d98067e2d851a8bb4cac2Location:ControllerNumber: 6UniqueId:Identifier: bus=0x0006, vendor=0x1234, product=0x5678, version=0x0001KeyLayoutFile: /system/usr/keylayout/Generic.klKeyCharacterMapFile: /system/usr/keychars/Generic.kcmConfigurationFile:HaveKeyboardLayoutOverlay: falseVideoDevice: <none>
Input Reader State:......Device 10: virtual_input_device
Input Dispatcher State:......Display: 5Windows:Display: 0

相关文档

触摸设备 | Android 开源项目 | Android Open Source Project
Linux 输入事件代码
Linux 多点触控协议
Linux 输入驱动程序

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

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

相关文章

Vue核心 列表渲染 数据监视

1.13.列表渲染 1.13.1.基本列表 v-for指令 用于展示列表数据语法&#xff1a;&#xff0c;这里key可以是index&#xff0c;更好的是遍历对象的唯一标识可遍历&#xff1a;数组、对象、字符串&#xff08;用的少&#xff09;、指定次数&#xff08;用的少&#xff09; <!…

对传递函数的零极点、频率响应、稳定性的理解

对传递函数的零极点、频率响应、稳定性的理解 零极点 从传递函数求零极点 令传递函数分子为0求出零点&#xff0c;令分母为0求出零点。 频率响应 单极点系统的频率响应 A v A v d c ∗ ( 1 / ( 1 s R C ) ) AvAv_dc*(1/(1sRC)) AvAvd​c∗(1/(1sRC))&#xff0c;系统的极…

Flask 知识点整理

文章目录 1.URL与视图的映射带参数的url&#xff1a;将参数固定到了path中查询字符串的方式传参 2.模板渲染给html文件传参 3.模板访问对象属性User 是个类User是个字典 4.过滤器的使用通过一个 | 管道符进行过滤自定义过滤器 5.控制语句iffor Flask连接MySQL数据库ORM模型与表…

[MySQL / Mariadb] 数据库学习-Linux中二进制方式安装MySQL5.7

Linux中二进制方式安装MySQL5.7 安装安装方式官网下载安装包创建用户组mysql&#xff0c;用户和目录把下载的安装包&#xff0c;放到/home/mysql/将本地文件拷贝到远程&#xff1a; scp 文件名 –用户名计算机IP或者计算机名称:远程路径 验证包解压安装包&#xff0c;移动到/us…

OJ 系统常用功能介绍 快速入门 C++ Python JAVA语言在线评测

技术支持微 makytony 服务器配置 腾讯云 2H4G 5M 60GB 轻量应用服务器 承载大约 200~400人使用&#xff0c;经过压力测试&#xff0c;评测并发速度可满足130人左右的在线比赛。 系统镜像选 Ubuntu 22.04 LTS &#xff0c;Ubuntu是最热门的Linux发行版之一&#xff0c;是一款…

数组中的empty剖析

数组中的empty剖析 一、首先empty是怎么来的 直接通过new Array来新建&#xff0c;手动修改数组的length&#xff0c;逗号之间没有任何数据等 const array new Array(3); console.log(array); //* (3) [empty 3]const array2 [1, , 3]; console.log(array2); //* [1, e…

一起学 WebGL:绘制图片

大家好&#xff0c;我是前端西瓜哥。之前讲解了如何用 WebGL 绘制红色三角形&#xff0c;今天西瓜哥带大家来学习如何将图片绘制到画布上的技术&#xff1a;纹理映射&#xff08;texture mapping&#xff09;。 本文为系列文章中的一篇&#xff0c;请先阅读&#xff1a; 《一起…

python 系列 07 - 基于easyocr的ocr识别

OCR,光学文字识别&#xff0c;对文本资料进行扫描&#xff0c;然后对图像文件进行分析处理&#xff0c;获取文字及版面信息的过程。本示例通过easyocr库来演示。easyocr是一个比较流行的库&#xff0c;支持超过80种语言。安装的时候注意会附带安装torch库&#xff08;一个深度学…

【Python】序列类型③-集合

文章目录 1.集合(set)简介2.集合的定义3.集合的遍历4.集合的常用方法 1.集合(set)简介 集合是一种无序可变的容器对象 集合最大的特点:同一个集合内元素是不允许有重复的,因此集合自带"去重"效果 2.集合的定义 集合的定义有两种方式: 使用{}进行定义,这种方式不能定…

获得 随机验证码(以图片为底层)

1&#xff1a;工具类 Slf4j public class RandomValidateCode {private static String baseNumLetter "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";private static String font "微软雅黑";//绘制验证码图片,返回验证码文本内容pu…

基于海鸥算法改进的随机森林分类算法-附代码

基于海鸥算法改进的随机森林分类算法 文章目录 基于海鸥算法改进的随机森林分类算法1.数据集2.RF模型3.基于海鸥算法优化的RF4.测试结果5.Matlab代码6.Python代码 摘要&#xff1a;为了提高随机森林数据的分类预测准确率&#xff0c;对随机森林中的树木个数和最小叶子点数参数利…

版本控制系统Git - 配置与基本使用

Git 1 Git简介1 Git概述2 Git的作用2.1 项目版本管理2.2 多人协同开发2.3 Git 的结构2.4 Git的工作原理 2 Git安装1 下载Git2 安装Git3 配置环境变量4 测试git是否安装成功5 安装git桌面工具(可以不安装) 3 Git基本操作1 设置Git用户2 新建仓库3 查看仓库状态4 添加到暂存文件5…

【python可视化】常用数据类型

&#x1f64b;‍ 哈喽大家好&#xff0c;本次是python数据分析、挖掘与可视化专栏第二期 ⭐本期内容&#xff1a;常用数据类型 &#x1f3c6;系列专栏&#xff1a;Python数据分析、挖掘与可视化 &#x1f44d;欢迎大佬指正&#xff0c;一起学习&#xff0c;一起加油&#xff01…

C++入门3(C++新特性 using string auto)

C入门3 C新特性auto推导规则auto 作为函数的形参类型decltype基于范围for循环 typedef与usingC语言定义变量typedef 在C语言中的写法using在C11中的写法using与template的结合 string的简单使用 C新特性 auto推导规则 auto类型推导: auto定义的变量&#xff0c;可以根据初始化…

C语言单链表

本节目标&#xff1a; ①定义单链表结构体 ②初始化单链表 ③单链表增加结点&#xff08;头插法、尾插法&#xff09; ④删除指定结点 ⑤打印输出 目录 导入头文件 定义单链表结构体 初始化单链表 头插法 尾插法插入 删除指定结点 打印单链表 全部代码展示 导入头文件 …

中文大模型安全性哪家强?清华团队新发布

当前大型语言模型的火爆程度我们不用再进行赘述了&#xff0c;伴随着百度文心一言打响国内商业大模型第一枪&#xff0c;华为盘古&#xff0c;阿里通义千问&#xff0c;智谱ChatGLM,科大讯飞星火等国内公司纷纷开始布局。 另一方面由于众所周知的政策原因&#xff0c;和如火如荼…

5 分钟教你如何免费用上 GPT-4

今天要分享的就是普通用户&#xff0c;没有 OpenAI 账号&#xff0c;不需要写代码&#xff0c;你依然可以免费体验 GPT-4&#xff0c;当然&#xff0c;会有一些缺点&#xff0c;本篇文章将会手把手教你怎么用上免费版的 GPT-4 以及它的一些限制。 第一步&#xff1a;打开 Stea…

Threejs进阶之十三:CSS3DRenderer与Tween.js实现粒子小球按规律变化

今天我们使用CSS3DRendererTween.js实现Threejs官方示例中的粒子小球按规律变化的效果&#xff0c;先看下最终实现的效果 先来分析下&#xff0c;这个页面的动画效果是由512个小球组合起来的四种不同变化&#xff0c;分别是曲面、立方体、随机和圆球四种变化&#xff1b;下面我…

UDP的报文结构

UDP 报文结构 基本上所有的教科书上都是这样画的图, 但实际上 UDP 报文结构不是这样的, 这样显示应该是容易排版. 正确应该如下图 : 端口号 : 每个端口号在 UDP 报文里占两个字节, 取值范围就是: 0 ~ 65535 源 ip 和源端口描述了数据从哪里来, 目的 ip 和目的端口描述了数据去哪…

文本的清洗和标准化:如何处理混乱的数据?

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…