目录

一、目的

最近接触到了一个问题:耳机插入事件的由来,走读了下IMS输入系统服务的源码。同时,IMS输入系统服务在Android的开发过程中,也经常出现,有必要了解下相关原理。

  1. 学习下IMS输入系统的源码设计,了解该模块承担的业务职责,熟悉Android结构
  2. 了解Android屏幕点击事件、物理按键事件的分发规则

二、环境

  1. 版本:Android 11
  2. 平台:高通 QCM2290

三、相关概念

3.1 输入设备

常见的输入设备有鼠标、键盘、触摸屏等,用户通过输入设备与系统进行交互。

3.2 UEVENT机制

"uevent" 是 Linux 系统中的一种事件通知机制,用于向用户空间发送有关内核和设备状态变化的通知。这种机制通常用于设备驱动程序、热插拔事件以及设备状态变化等场景,以便用户空间应用程序能够在这些事件发生时做出相应的响应。

3.3 JNI

JNI,全称Java Native Interface,是Java编程语言的一种编程框架,用于实现Java代码与其他编程语言(如C、C++)进行交互的接口。JNI允许Java程序调用原生代码(native code),即由其他编程语言编写的代码,并且允许原生代码调用Java代码。通过JNI,Java程序可以访问底层系统功能、使用硬件设备、调用第三方库等。

3.4 EPOLL机制

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

3.5 INotify

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

四、详细设计

通过屏幕的触摸事件,来分析IMS系统,相关如下

4.1 结构图

4.2 代码结构

层级 模块 描述 源码 编译产物
Framework InputManagerService xxx frameworks/base/services/core/java/ out/target/product/qssi/system/framework/services.jar
Native NativeInputManager xxx frameworks/base/services/core/jni/ out/target/product/qssi/system/lib64/libandroid_servers.so
Native Inputflinger xxx frameworks/native/services/inputflinger/ out/target/product/qssi/system/lib64/libinputflinger.so
Native Inputreader xxx frameworks/native/services/inputflinger/reader out/target/product/qssi/system/lib64/libinputreader.so
Native Inputdispatcher xxx frameworks/native/services/inputflinger/dispatcher/ (静态库)out/soong/.intermediates/frameworks/native/services/inputflinger/dispatcher/libinputdispatcher/android_arm64_armv8-a_static/libinputdispatcher.a
Native NativeInputEventReceiver xxx frameworks/base/core/jni/ out/target/product/qssi/system/lib64/libandroid_runtime
Native InputChannel xxx frameworks/native/libs/input/ out/target/product/qssi/system/lib64/libinput.so

4.3 InputManagerService模块

InputManagerService是Android框架层一个非核心服务,主要是提供一个IMS输入系统启动的入口,同时对应用层提供业务相关接口。

4.3.1 IMS服务入口

Android设备开机后,会启动system_server进程,InputManagerService服务(以下简称IMS)在该进程被唤起。

@frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
t.traceBegin("StartInputManagerService");
inputManager = new InputManagerService(context);//新建IMS实例
t.traceEnd();
...
t.traceBegin("StartInputManager");
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());//设置窗体事件监听
inputManager.start();//启动IMS服务
t.traceEnd();
...
}

4.3.2 IMS初始化

此处做一些IMS相关的初始化操作,会调用nativeInit方法,获取一个NativeInputManager对象,类似于一个句柄。

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
public InputManagerService(Context context) {
...
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());
...
}

4.3.3 IMS启动

InputManagerService通过start方法启动,会调用nativeStart方法,该方法为Native方法

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
private static native void nativeStart(long ptr);
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr); // Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}

4.3.4 IMS消息监听

该方法为Native的回调方法,用于上报IMS事件,如耳机插入事件等。

@frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
// Native callback.
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
...
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
} if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
} if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask);
}
...
}

4.4 NativeInputManager模块

该模块为JNI模块,主要处理Java方法与c++方法映射关系,即IMS服务与InputFlinger模块的通信桥梁。

4.4.1 nativeInit初始化

(1)新建一个NativeInputManager对象,并将该对象返回给java层

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

(2)创建InputManager管理类,主要用于管理Input事件分发、事件读取行为

@\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();
...
mInputManager = new InputManager(this, this);
defaultServiceManager()->addService(String16("inputflinger"),
mInputManager, false);
}

4.4.2 nativeStart启动

获取上一个阶段创建NativeInputManager对象,并引用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);
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}

4.5 Inputflinger模块

input事件的管理类,数据传递类,也是输入系统native层核心的模块。

ps: 根据字典里的定义,flinger是指出轨的人。在SurfaceFlinger的例子中,它把可视数据扔给surface AudioFlinger把音频数据扔给适当的接收者。它们只是“可爱”的词…

4.5.1 启动事件管理服务

启动两个核心的阻塞线程,一个是事件分发线程,一个是事件读取线程。

@frameworks\native\services\inputflinger\InputManager.cpp
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;
}

4.6 Inputreader模块

事件读取服务,读取驱动上报事件

4.6.1 启动InputReader线程

(1)创建一个InputThread线程

@frameworks\native\services\inputflinger\reader\InputReader.cpp
status_t InputReader::start() {
if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
return OK;
}

(2)InputThread线程的loop循环队列(线程和loop的关系)

@frameworks\native\services\inputflinger\reader\InputReader.cpp
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
...
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//step 1. 通过EventHub抽取事件列表
{ // acquire lock
...
if (count) {
processEventsLocked(mEventBuffer, count);// step 2. 对事件进行加工处理
}
...
} // release lock
...
mQueuedListener->flush();//step 3. 事件发布
}

4.6.2 EventHub获取事件队列

EventHub:事件集线器,它将全部的输入事件通过一个接口getEvents(),将从多个输入设备节点中读取的事件交给InputReader,是输入系统最底层的一个组件。

(1)EventHub的构造函数

        它通过INotifyEpoll机制建立起了对设备节点增删事件以及可读状态的监听。同时,EventHub创建了一个名为wakeFds的匿名管道,因为InputReader在运行getEvents()时会因无事件而导致其线程堵塞在epoll_wait()的调用里,然而有时希望能够立马唤醒InputReader线程使其处理一些请求。

@frameworks\native\services\inputflinger\reader\EventHub.cpp
static const char* DEVICE_PATH = "/dev/input";
EventHub::EventHub(void)
: mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
mOpeningDevices(nullptr),
mClosingDevices(nullptr),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
mPendingEventCount(0),
mPendingEventIndex(0),
mPendingINotify(false) {
ensureProcessCanBlockSuspend(); mEpollFd = epoll_create1(EPOLL_CLOEXEC);//创建一个epoll对象,用来监听设备节点是否有事件
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); mINotifyFd = inotify_init();//创建一个inotify对象,用来监听设备节点的增删事件
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
...
struct epoll_event eventItem = {};
eventItem.events = EPOLLIN | EPOLLWAKEUP;
eventItem.data.fd = mINotifyFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);//将mINotifyFd注册进epoll对象中
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2];
result = pipe(wakeFds);//创建一个匿名管道,用于唤醒EventHub,避免无事件引起阻塞
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
...
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);//将管道读取端加入epoll对象中
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}

mEpollFd监听如下几个事件:设备节点的增加、删除、修改;匿名管道,避免无事件阻塞

(2)RawEvent结构体

        mEventBuffer用于描述原始输入事件,其类型为RawEvent,相关结构体如下:

@frameworks\native\services\inputflinger\reader\include\EventHub.h
/*
* A raw event as retrieved from the EventHub.
*/
struct RawEvent {
nsecs_t when;//事件时间戳
int32_t deviceId;//产生事件的设备ID
int32_t type;//事件类型
int32_t code;//事件编码
int32_t value;//事件值
};

(3)EventHub->getEvents事件,

        getEvents()是事件处理的核心方法,其通过EPOLL机制和INOTIFY,从多个设备节点读取事件。

@frameworks\native\services\inputflinger\reader\EventHub.cpp
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
...
for (;;) {
...
if (mNeedToScanDevices) {//Step 1.扫描设备
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
...
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) { //Step 2.处理未被InputReader取走的输入事件与设备事件
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
...
// This must be an input event
if (eventItem.events & EPOLLIN) {
int32_t readSize =
read(device->fd, readBuffer, sizeof(struct input_event) * capacity);//Step 3.读取底层上报事件
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
...
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {//构建需要上报的事件
struct input_event& iev = readBuffer[i];
event->when = processEventTimestamp(iev);
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;//将event指针移动到下一个可用于填充事件的RawEvent对象
capacity -= 1;
}
...
}
}
...
}
...
mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);//Step 4.阻塞,等待事件各种类型消息
mLock.lock(); // reacquire lock after poll
...
}
// All done, return the number of events we read.
return event - buffer;
}

Step 1. 扫描设备,会获取input/dev/下的所有设备,并将各个设备注册到epoll线程池里,监听各个设备的消息状态;

Step 2. 处理未被InputReader取走的输入事件与设备事件,一般情况下有事件上报时,epoll_wait会读取到mPendingEventItems值,即mPendingEventCount值,即会进入该流程;

Step 3. 读取底层上报事件,根据上报的fd设备,读取对应的设备节点。即可以获取到上报的事件内容。如下为屏幕点击对应的上报事件:

Step 4. 通过epoll机制,阻塞当前进程,等待设备节点变更,事件上报。

4.6.3 Input事件加工

主要是将底层RawEvent事件,进一步加工,将Event事件注入到mArgsQueue队列的过程。

(1)Input事件加工

@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;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
...
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//输入事件
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED://设备增加
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED://设备移除
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN://设备扫描结束
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
} }

(2)Input事件推送

该流程业务代码比较冗长,做了层层封装,如下为方法调用栈:

InputReader.processEventsLocked() -> InputReader.processEventsForDeviceLocked() -> InputDevice.process() -> MultiTouchInputMapper.process() -> TouchInputMapper.process()->TouchInputMapper.sync() -> TouchInputMapper.processRawTouches() -> TouchInputMapper.cookAndDispatch() -> TouchInputMapper.dispatchTouches() -> TouchInputMapper.dispatchMotion() -> QueuedInputListener -> notifyMotion()

最终可以看到事件最终会传递到mArgsQueue容器内。

@frameworks\native\services\inputflinger\InputListener.cpp
std::vector<NotifyArgs*> mArgsQueue;
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
traceEvent(__func__, args->id);
mArgsQueue.push_back(new NotifyMotionArgs(*args));
}

4.6.4 事件发布

(1)当事件加工完成后,会引用flush()方法,将事件发布出去

@frameworks\native\services\inputflinger\InputListener.cpp
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);//事件发布
delete args;
}
mArgsQueue.clear();
}

(2)由上一节可知,屏幕点击事件对应的args为NotifyMotionArgs

@frameworks\native\services\inputflinger\InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyMotion(this);
}

(3)大家可以自己去追溯下源码,该listener接口的实现类是InputDispatcher。至此,事件将进入下一阶段——事件分发。

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
...
}

4.7 Inputdispatcher模块

事件分发服务,将底层读到的事件,分发到上层

4.7.1 Input事件上报

至此,我们知道InputDispatch会启动一个阻塞线程,等待底层事件上报;而通过InputReader的分析,我们知道底层事件响应,最终会通知InputDispatch模块的notifyMotion()方法

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
...
{ // acquire lock
mLock.lock();
...
// Just enqueue a new motion event.
MotionEntry* newEntry =
new 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, 0, 0); needWake = enqueueInboundEventLocked(newEntry);//构建新的MotionEvent事件
mLock.unlock();
} // release lock if (needWake) {
mLooper->wake();//唤醒InputDispatch线程,进行分发
}
}

4.7.2 启动InputDispatcher线程

(1)创建一个InputDispatcher线程

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
status_t InputDispatcher::start() {
if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
return OK;
}

(2)InputThread线程的loop队列

@frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all(); // Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);//事件分发
}
...
} // release lock // Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);//堵塞,等待唤醒
}

(3)事件分发过程

事件的分发过程也比较冗长,此处不具体分析过程,其业务堆栈如下,即事件分发最终会下发到publishMotionEvent。

InputDispatcher.dispatchOnceInnerLocked() -> InputDispatcher.dispatchMotionLocked() -> InputDispatcher.dispatchEventLocked() -> InputDispatcher.prepareDispatchCycleLocked() -> InputDispatcher.enqueueDispatchEntriesLocked() -> InputDispatcher.startDispatchCycleLocked() -> InputPublisher.publishMotionEvent()

@frameworks\native\libs\input\InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, float xScale, float yScale, float xOffset,
float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
...
InputMessage msg;
msg.header.type = InputMessage::Type::MOTION;
msg.body.motion.seq = seq;
msg.body.motion.eventId = eventId;
...
return mChannel->sendMessage(&msg);
}

4.8 WindowManagerService模块

4.8.1 ViewRootImpl阶段

InputDispatcher通过InputChannel将事件发送到目标窗口的进程了。那么目标窗口是如何接收传递事件呢?

(1)Activity创建窗口相关阶段介绍

attach阶段:

一个Activity 创建了一个PhoneWindow对象 ,PhoneWindow通过setWindowManager() 创建了WindowManagerImpl 。

即Activity 对应一个PhoneWindow,并得到了一个WindowManager(WindowManagerImpl,Window创建的)。

onCreate阶段:

创建了DecorView ,并将 activity的布局添加到DecorView中 。

onResume阶段:

创建了ViewRootImpl,通过setView()最终由Session进入system_server进程。最终执行addWindow添加窗口到WMS。

(2)ViewRootImpl.setView()

@frameworks\base\core\java\android\view\ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
...
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();//创建inputChannel对象
}
try {
...
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);//通过session跨进程调用WMS的addWindow方法给inputChannel赋值
setFrame(mTmpFrame);
}
...
if (inputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());//创建mInputEventReceiver对象,用于App侧接收Input事件
}
...
}
}
}

4.8.2 WindowManagerService.addWindow()

(1)openInputChannel():生成一对inputChannel,并返回一个对象给App端。

Session.addToDisplayAsUser() -> WindowManagerService.addWindow() -> EmbeddedWindow.openInputChannel()

@frameworks\base\services\core\java\com\android\server\wm\EmbeddedWindowController.java
InputChannel openInputChannel() {
final String name = getName(); final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//InputChannel底层通过一对socket进行通信
mInputChannel = inputChannels[0];
final InputChannel clientChannel = inputChannels[1];
mWmService.mInputManager.registerInputChannel(mInputChannel);//将一个inputChannel对象注册到Input的Native端
...
return clientChannel;//返回一个inputChannel对象给App端
}

(2)openInputChannelPair():创建一对通过socket通信的inputChannel对象。

InputChannel.openInputChannelPair() -> InputChannel.nativeOpenInputChannelPair() -> android_view_InputChannel.android_view_InputChannel_nativeOpenInputChannelPair() -> InputTransport.openInputChannelPair()

@frameworks\native\libs\input\InputTransport.cpp
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
ALOGE("channel '%s' ~ Could not create socket pair. errno=%d",
name.c_str(), errno);
outServerChannel.clear();
outClientChannel.clear();
return result;
} 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);//server端 std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);//client端
return OK;
}

4.8.3 WindowInputEventReceiver

app进程和system_server进程通过socket通信,底层捕获的事件最终通过inputChannel模块来实现,再由app端的WindowInputEventReceiver去接收,最后把事件分发到目标View上。

(1)WindowInputEventReceiver构造函数

注册一个事件接收器,WindowInputEventReceiver的父类是InputEventReceiver

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);//初始化操作 mCloseGuard.open("dispose");
} // Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
private void dispatchInputEvent(int seq, InputEvent event) {//native层事件回调方法
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);//事件分发到各个目标View上
}

(2)nativeInit

由上可知,在添加窗口时,WMS会针对于每个窗口设置一对InputChannel对象,分为client端和server端,其中server端在system_server进程,client端在app进程。我们需要去监听client端,以期能够捕获server端的事件消息。

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
...
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();//初始化
...
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
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();//此fd为WMS创建的InputChannel的client端
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);//注册监听
} else {
mMessageQueue->getLooper()->removeFd(fd);//移除监听
}
}
}

(3)handleEvent

当server端写入事件时,client端的looper就能被唤醒,会调用handleEvent函数(当fd可读时,会调用LooperCallback的handleEvent,而NativeInputEventReceiver继承自LooperCallback,所以这里会调用NativeInputEventReceiver的handleEvent函数,线程和looper的关系此处不展开)

@frameworks\base\core\jni\android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
...
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);//处理事件
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
...
return 1;
} status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
...
for (;;) {
...
if (!skipCallbacks) {
...
if (inputEventObj) {
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);//事件消息回调java层
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
env->DeleteLocalRef(inputEventObj);
}
}
...
}
}

五、Input设备节点介绍

5.1 常见触摸事件类型

事件类型 事件名称 事件编码 事件定义
EV_SYN 同步事件 0004 or 0005 代表一个事件开始(不必要)
EV_SYN 同步事件 SYN_REPORT 代表一个事件结束(必要的)
EV_ABS 绝对坐标的事件 ABS_MT_SLOT 本质代表着不同的手指,他的value代表手指id
EV_ABS 绝对坐标的事件 ABS_MT_TRACKING_ID 类协议特有的,每个slot会和一个ID相对应,一个非负数表示一次接触,ffffffff表示一次接触结束,即手指抬起。无论在接触的类型相对应的slot发生改变,驱动都应该通过改变这个值来使这个slot失效,并且下一次触摸的ID值会是这次的值加1
EV_ABS 绝对坐标的事件 ABS_MT_POSITION_X 相对于屏幕中心的x坐标
EV_ABS 绝对坐标的事件 ABS_MT_POSITION_Y 相对于屏幕中心的y坐标
EV_ABS 绝对坐标的事件 ABS_MT_TOUCH_MAJOR 接触部分的长轴长度,相当于椭圆的长轴
EV_ABS 绝对坐标的事件 ABS_MT_TOUCH_MINOR 接触部分的短轴长度,相当于椭圆的短轴
EV_ABS 绝对坐标的事件 ABS_MT_PRESSURE 代表按下压力,有的设备不一定有
EV_KEY 按键事件 BTN_TOUCH 触碰按键,其值是DOWN或者UP
EV_KEY 按键事件 BTN_TOOL_FINGER 按键的是finger,其值是DOWN或者UP

5.2 getevent

adb shell getevent -lt

5.3 sendevent

模拟按压音量键+

//通过getevent指令,获取音量按键+的事件码
bengal:/ # getevent
add device 1: /dev/input/event4
name: "bengal-scubaidp-snd-card Button Jack"
add device 2: /dev/input/event3
name: "bengal-scubaidp-snd-card Headset Jack"
add device 3: /dev/input/event0
name: "qpnp_pon"
add device 4: /dev/input/event1
name: "gpio-keys"
add device 5: /dev/input/event2
name: "sitronix_ts_i2c"
/dev/input/event1: 0001 0073 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0073 00000000
/dev/input/event1: 0000 0000 00000000 //通过sendevent模拟音量键+的事件
130|bengal:/ # sendevent /dev/input/event1 1 115 1
bengal:/ # sendevent /dev/input/event1 0 0 0
bengal:/ # sendevent /dev/input/event1 1 115 0
bengal:/ # sendevent /dev/input/event1 0 0 0
bengal:/ #

ps:getevent获取到的事件码为16进制,sendevent输入的值为10进制,需要注意下!!!

六、参考资料

https://liuwangshu.blog.csdn.net/article/details/84883156

https://liuwangshu.blog.csdn.net/article/details/86771746

https://www.cnblogs.com/brucemengbm/p/7072395.html

事件分发介绍:

https://www.cnblogs.com/fanglongxiang/p/14091511.html

InputChannel介绍:

https://blog.csdn.net/ztisen/article/details/130188132

GetEvent指令介绍:

https://blog.csdn.net/Gary1_Liu/article/details/124675608

Android 输入系统介绍的更多相关文章

  1. android输入限制

    title: 2016-5-30 Android输入验证 tags: EditText,输入验证 --- 前言2 使用EditText让用户输入文字时,需要对输入验证.除过验证是否有效的逻辑不同,Ed ...

  2. Android核心分析之十五Android输入系统之输入路径详解

       Android用户事件输入路径 1 输入路径的一般原理 按键,鼠标消息从收集到最终将发送到焦点窗口,要经历怎样的路径,是Android GWES设计方案中需要详细考虑的问题.按键,鼠标等用户消息 ...

  3. [Android] 输入系统(一)

    Android输入系统是人与机器交互最主要的手段.我们通过按键或者触碰屏幕,会先经由linux产生中断,进行统一的处理过后,转换成Android能识别的事件信息,然后Android的输入系统去获取事件 ...

  4. Linux/Android——输入子系统input_event传递 (二)【转】

    本文转载自:http://blog.csdn.net/jscese/article/details/42099381 在前文Linux/Android——usb触摸屏驱动 - usbtouchscre ...

  5. Android 输入管理服务-输入事件到达之后的处理流程

    接上一篇博客"Android 输入管理服务启动过程的流程".这两天分析了Android 输入管理服务接收到输入事件之后的处理流程,详细流程例如以下面两图所看到的: 接下图

  6. 《深入理解Android 卷III》第五章 深入理解Android输入系统

    <深入理解Android 卷III>即将公布.作者是张大伟.此书填补了深入理解Android Framework卷中的一个主要空白.即Android Framework中和UI相关的部分. ...

  7. 10.7 android输入系统_Dispatcher线程情景分析_Reader线程传递事件和dispatch前处理

    android输入系统C++最上层文件是com_android_serve_input_InputManagerService.cpp global key:按下按键,启动某个APP可以自己指定,修改 ...

  8. 10.5 android输入系统_Reader线程_使用EventHub读取事件和核心类及配置文件_实验_分析

    4. Reader线程_使用EventHub读取事件 使用inotify监测/dev/input下文件的创建和删除 使用epoll监测有无数据上报 细节: a.fd1 = inotify_init(& ...

  9. 10.4 android输入系统_框架、编写一个万能模拟输入驱动程序、reader/dispatcher线程启动过程源码分析

    1. 输入系统框架 android输入系统官方文档 // 需FQhttp://source.android.com/devices/input/index.html <深入理解Android 卷 ...

  10. 10.1、android输入系统_必备Linux编程知识_inotify和epoll

    1. inotify和epoll 怎么监测键盘接入与拔出? (1)hotplug机制:内核发现键盘接入/拔出==>启动hotplug进程==>发消息给输入系统 (2)inotify机制:输 ...

随机推荐

  1. MQ消息队列篇:三大MQ产品的必备面试种子题

    MQ有什么用? MQ(消息队列)是一种FIFO(先进先出)的数据结构,主要用于实现异步通信.削峰平谷和解耦等功能.它通过将生产者生成的消息发送到队列中,然后由消费者进行消费.这样,生产者和消费者之间就 ...

  2. [jenkins]简介与安装

    前言 jenkins是一种代码构建平台,一般用于CI/CD中的CI部分,当然也可以集成CD功能. 安装 环境 IP:192.168.0.10 系统:centos 7 快速安装步骤 官网下载jenkin ...

  3. pandas读取mysql并导出为excel

    前言 业务需要从数据库导出数据为excel,并设置成自动化.这里用pandas写的数据导入导出,还算方便.配合crontab + shell脚本使用,每天晚上自动生成excel,然后cp到指定目录.s ...

  4. DirtyCow 脏牛提权漏洞(CVE-2016-5195)

    描述: 该漏洞是 Linux 内核经典漏洞,内核内存子系统在处理写时拷贝(Copy-on-Write)时存在条件竞争漏洞, 导致可以破坏私有只读内存映射.黑客可以在获取低权限的的本地用户后,利用此漏洞 ...

  5. auto-GPT部署

    Auto-GPT 是一个实验性开源应用程序,其作者在3月31日将其发布在Github上.它以GPT-4 作为驱动,可以自主做出决定以实现目标,无需用户干预.AutoGPT的地址:https://git ...

  6. 为 VitePress 网站添加 RSS 订阅支持

    省流:使用 vitepress-plugin-rss 这个插件 前言 在看许多个人博客站点的时候,右上角总会有个RSS订阅的标志 恰好我的博客也是基于 VitePress 搭建的,就想看看能不能也实现 ...

  7. 【路由器】OpenWrt 手动编译 ipk

    目录 .ipk 文件 编译准备 编译 .ipk 文件 更新 feeds 配置平台 获取交叉编译链 添加需要编译的第三方软件包 参考资料 .ipk 文件 .ipk 文件是可以通过 OpenWrt 的包管 ...

  8. 最接地气的.NET微服务框架

    前言: "人必有所执,方能有所成",从2018年底我就开始规划要写一个.NET微服务框架,5年了,今天终于正式发布了. 正文: Wing 致力于打造一个功能强大.最接地气的.NET ...

  9. Solution -「营业」「CF 527C」Glass Carving

    Description Link. 有一个块 \(n\times m\) 的矩形,有 \(q\) 次操作,每次把矩形横 / 竖着切一刀,问切完后的最大矩形面积. Solution 一个不同于大多数人. ...

  10. linux特殊权限rws和rwt

    Linux文件,除了rwx这些权限外,还有一些特殊的权限,如rws.rwt. 1.s权限(setuid) 1.1 设置方法:chmod u+s 该位可以让普通用户以root用户的角色运行只有root帐 ...