IMS初始化
一、简介
当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent ;最后,交付给相应的目标窗口(Window)来消费该输入事件
二、对象介绍
- frameworks/native/services/inputflinger/reader/InputReader.cpp
- frameworks/native/services/inputflinger/reader/EventHub.cpp
- frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
- frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
- frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
- frameworks/native/services/inputflinger/InputManager.cpp
三**、IMS初始化分析**
InputManagerService
位于SystemServer.java#startOtherServices
方法中被启动,源码如下
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
InputManagerService inputManager = null;
...
t.traceBegin("StartInputManagerService");
inputManager = new InputManagerService(context);
t.traceEnd();
...
// WMS与:InputManagerService/ActivityTaskManager/PhoneWindowManager相互绑定
// WMS创建时会基于DisplayThread,也就是WMS是运行在android.display线程中
wm = WindowManagerService.main(context, **inputManager**, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// 将IMS加入ServiceManager中统一管理
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
...
inputManager.start();
所以IMS的执行顺序为:new InputManagerService(context);
、inputManager.start();
并且属于systemServer线程,继续看构造方法
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputManagerService(Context context) {
this.mContext = context;
// 运行在android.display线程,这个handler很重要
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
...
//初始化native对象
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
...
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
this.mHandler是非常重要的,运行在android.display线程上,跟WMS所处的线程一样,主要用于以下几个方面:
- 接收和处理输入事件:mHandler会接收来自底层硬件(如触摸屏、按键等)的原始输入事件,并将其封装成消息发送给mHandler处理。这样可以将输入事件的处理从主线程转移到后台线程(android.display)
- **分发输入事件:**mHandler会将接收到的输入事件分发给相应的窗口或应用程序进行处理。通过消息的处理和分发机制,确保输入事件能够顺利传递给正确的目标,实现用户输入的精确响应。
- **处理输入事件队列:**mHandler会维护一个输入事件队列,将接收到的输入事件按照一定的顺序存储在队列中,并逐个处理。这样可以保证输入事件的有序性,避免输入事件的丢失或混乱。
总而言之,mHandler在InputManagerService中的作用是接收、处理和分发输入事件,实现用户输入的响应和传递。通过将输入事件的处理从主线程转移到android.display线程,systemServer线程会等待display线程处理完成后才会继续执行
继续看看nativeInit
方法,位于:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
类名叫:NativeInputManager
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj/*IMS*/, jobject contextObj/*context*/, jobject messageQueueObj/*MessageQueue*/) {
// messageQueueObj就是上层调用的:mHandler.getLooper().getQueue()
// 该handler属于android.display线程
// 这段代码定义了一个名为sp的变量,它的类型是一个智能指针,指向一个MessageQueue对象。智能指针是一种封装了指针的对象,
// 它具有自动管理内存的能力,即在不需要使用该指针时会自动释放它所指向的内存空间,
// 避免了内存泄漏的问题。这里使用的是Android系统提供的sp智能指针,它是一个引用计数智能指针,可以在多个线程间安全地传递和共享
// 这里获取了Message队列
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
//NativeInputManager中创建了InputManager
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
先介绍一下方法里的形参:
JNIEnv* env
:JNI对象,native方法必须也有的,该参数无需开发者去关心(只有JNI开发时会用到里面的参数和方法,此处不做关心)serviceObj
:InputManagerService.java的实例对象contextObj
:IMS中的context上下文messageQueueObj
:mHandler中Looper里管理的队列
以上代码只关注:获取了messageQueue
和创建了NativeInputManager
对象
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper/*messageQueue的looper*/) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
// mServiceObj就是InputManagerService
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;
// 初始化InputManager,创建InputManager对象
InputManager* im = new InputManager(this, this);
mInputManager = im;
defaultServiceManager()->addService(String16("inputflinger"), im);
}
然后继续创建了InputManager
对象,并将im加入服务队列中,继续看看IM对象的构造方法
frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 创建InputDispatcher
// 创建InputReader
// InputReader读取/dev/input节点,然后将数据传递给InputDispatcher
// 由InputDispatcher来分发事件到window
// readerPolicy和dispatcherPolicy都是NativeInputManager对象,NativeInputManager实现了这两个接口
// 就饿可以通过Policy来回调数据给NativeInputManager了
mDispatcher = createInputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
// 包含了EventHub和InputListenerInterface及readerPolicy
mReader = createInputReader(readerPolicy, mClassifier);
}
// 创建InputReader,并创建了EventHub
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) {
return new InputReader(std::make_unique<EventHub>(), policy, listener);
}
// 创建InputDispatcher
sp<InputDispatcherInterface> createInputDispatcher(
const sp<InputDispatcherPolicyInterface>& policy) {
return new android::inputdispatcher::InputDispatcher(policy);
}
1.记住两个重要的接口InputReaderPolicyInterface
、InputDispatcherPolicyInterface
,他们的具体实现在NativeInputManager
中,也就是com_android_server_input_InputManagerService.cpp中。
2.然后创建了InputDispatcher
和InputReader
,并在InputReader
创建过程中同步创建了EventHub
3.InputReader
:读取/dev/input节点,然后将数据传递给InputDispatcher
4.InputDispatcher
:接收来自InputReader
的事件,然后由InputDispatcher来分发事件到window
继续看看EventHub的构造方法,看看有什么作用
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();
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
// inotify是Linux内核提供的一种文件系统监控机制,它可以监测文件或目录的变化,并通过特定的事件通知机制通知进程
mINotifyFd = inotify_init();
// 此处DEVICE_PATH为"/dev/input",监听该设备节点创建与删除操作
// 屏幕的所有事件都会经过处理存放进/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;
// 将mINotifyFd添加到fd池中
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];
// 调用了 pipe() 函数来创建一个无名管道,并将管道的读取端口和写入端口的文件描述符分别存储在 wakeFds[0] 和 wakeFds[1] 中。
// 这个管道可以用于进程间通信,其中一个进程将数据写入管道,而另一个进程则可以从管道中读取这些数据。
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
// 将pipe的读设置为非阻塞方式
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
// 将pipe的写设置为非阻塞方式
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;
// 添加管道的读端到epoll实例
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);
}
作用总结:创建文件系统监控机制,它可以监测/dev/input
的变化(因为从屏幕触碰的事件都会存入这个驱动节点中),创建管道用于进程间通信(文件描述符存放到wakeFds[0] 和 wakeFds[1]
中),其中一个进程将数据写入管道,而另一个进程则可以从管道中读取这些数据, 再将pipe的读/写设置为非阻塞方式。
创建监控驱动节点变化的文件描述符,通过监听驱动节点的内容,将内容放入管道,进行两端通信
frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub/*eventHub*/,
const sp<InputReaderPolicyInterface>& policy/*NativeInputManager*/,
const sp<InputListenerInterface>& listener)
: mContext(this),
mEventHub(eventHub),
mPolicy(policy)...{
// 创建了QueuedInputListener--->创建输入监听对象
// 此处mQueuedListener的成员变量mInnerListener便是InputDispatcher对象。
mQueuedListener = new QueuedInputListener(listener);
...
}
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
mInnerListener(innerListener) {
}
创建了QueuedInputListener
,里面有个成员变量mInnerListener
(InputListenerInterface),指向的回调就是InputDispatcher
对象,InputListenerInterface接口很重要,用于将输入事件封装成NotifyKeyArgs
对象回调给InputDispatcher
再来看看rameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
: mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mAppSwitchSawKeyDown(false),
mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(nullptr),
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
// mInTouchMode will be initialized by the WindowManager to the default device config.
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator),
mCompatService(getCompatService()) {
// 创建一个looper
mLooper = new Looper(false);
mReporter = createInputReporter();
mKeyRepeatState.lastKeyEntry = nullptr;
// 获取分发超时参数
policy->getDispatcherConfiguration(&mConfig);
}
- 创建属于自己线程的Looper对象;
- 配置超时机制,超时参数来自于IMS,参数默认值keyRepeatTimeout= 500,keyRepeatDelay = 50。
- keyRepeatTimeout的值为500 * 1000000LL,即500毫秒。这个值表示当用户按住一个键不放时,系统会在这个键按下后的500毫秒之后开始重复这个键的事件
- keyRepeatDelay的值为50 * 1000000LL,即50毫秒。这个值表示当用户按住一个键不放时,系统会在这个键按下后的50毫秒之后开始发送第一个重复事件
初始化就算完成了,再来总结一下:首先通过上层的IMS进入native层,创建NativeInputManager—>InputManager,然后创建了EventHub,InputReader,InputDispatcher,EventHub用于创建文件描述符来监听/dev/input
驱动节点的内容,再通过管道形式进行数据传递,InputReader创建了事件输入的监听器(QueuedInputListener),InputDispatcher创建了属于自己的looper和配置了超时机制。以上的输入事件均运行在android.display线程,因为looper是由IMS传递下来的在这里插入代码片