当前位置: 首页 > news >正文

Android 12系统源码_输入系统(二)InputManagerService服务

前言

上一篇我们简单介绍了Android系统输入事件的常见类型和事件的传递流程,输入事件的传递流程主要分三个部分,输入系统处理部分、WMS处理部分、View处理部分。
在这里插入图片描述
其中输入系统处理部分细分为输入子系统处理部分和InputManagerService处理部分,输入子系统主要是对事件源的原始信息进行采集,而InputManagerService则是监听dev/input下的所有设备节点,当设备节点有数据时会将数据进行加工处理并分发给合适的窗口,在讲解InputManagerService具体是如何处理输入事件的之前,我们需要先来认识一下InputManagerService这个服务。

一、IMS的启动

系统启动后会启动JVM虚拟机,SystemServer 是虚拟机的第一个进程,由init 进程fork 产生,主要用来启动frameworks层中的服务。和其他服务一样,InputManagerService服务也是在这里被启动的,下面我们简单列出SystemServer中和IMS启动相关的源码。

1.1 SystemServer阶段

frameworks/base/service/java/com/android/server/SystemServer.java

public final class SystemServer {private DisplayManagerService mDisplayManagerService;public static void main(String[] args) {new SystemServer().run();}private void run() {...代码省略...try {t.traceBegin("StartServices");startBootstrapServices(t);//引导服务startCoreServices(t);//核心服务startOtherServices(t);//其他服务} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;} finally {t.traceEnd(); // StartServices}...代码省略...}private void startOtherServices(@NonNull TimingsTraceAndSlog t) {...代码省略...WindowManagerService wm = null;InputManagerService inputManager = null;...代码省略...t.traceBegin("StartInputManagerService");inputManager = new InputManagerService(context);//注释1,创建InputManagerService对象t.traceEnd();...代码省略...t.traceBegin("StartWindowManagerService");// WMS needs sensor service readymSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);//注释2,WindowManagerService作为输入系统的中转站,其内部持有InputManagerService的引用wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);//将WindowManagerService添加到ServiceManagerServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);//将InputManagerService添加到ServiceManagerServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);t.traceEnd();...代码省略...t.traceBegin("StartInputManager");inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());inputManager.start();//注释3,启动IMS服务t.traceEnd();...代码省略...          }
}

SystemServer的startOtherServices方法先是在注释1处创建InputManagerService对象,接着在注释2处把IMS作为参数传给WMS以便其创建的实例对象包含有IMS的引用,然后将WMS和IMS都添加到了ServiceManager中进行统一管理,然后还会调用IMS的setWindowManagerCallbacks方法将输入事件回调和WMS进行关联,最后在注释3处调用IMS的start方法启动IMS。

1.2 InputManagerService的创建

1.2.1 InputManagerService构造方法

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {private final long mPtr;private static native long nativeInit(InputManagerService service,Context context, MessageQueue messageQueue);public InputManagerService(Context context) {this.mContext = context;this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//注释1,创建InputManagerHandler对象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());//注释2,调用nativeInit方法String doubleTouchGestureEnablePath = context.getResources().getString(R.string.config_doubleTouchGestureEnableFile);mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :new File(doubleTouchGestureEnablePath);LocalServices.addService(InputManagerInternal.class, new LocalService());}}

注释1处使用android.display线程的Looper创建了InputManagerHandler,这样InputManagerHandler便会运行在android.display线程中,android.display线程是系统共享的单例前台线程,这个线程内部执行了WMS的创建。
注释2处调用了nativeInit方法,此方法是一个Native方法,该方法会返回一个NativeInputManager对象指针地址,将该地址赋值给属性变量mPtr,这个属性变量后面后面我们还会提到。

1.2.2 nativeInit方法

>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;}//注释1,创建NativeInputManager对象NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());im->incStrong(0);return reinterpret_cast<jlong>(im);//注释2,返回对象指针
}// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,public virtual InputReaderPolicyInterface,public virtual InputDispatcherPolicyInterface,public virtual PointerControllerPolicyInterface {
protected:virtual ~NativeInputManager();
public:NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);inline sp<InputManagerInterface> getInputManager() const { return mInputManager; //返回对象实例 }
private:sp<InputManagerInterface> mInputManager;//InputManager对象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;//注释3,创建InputManager对象InputManager* im = new InputManager(this, this);mInputManager = im;defaultServiceManager()->addService(String16("inputflinger"), im);
}

在注释1处会创建NativeInputManager对象,然后在NativeInputManager的构造方法中,也就是注释3处,会创建InputManager对象,进一步通过inputManager来读取设备节点的增删事件和原始输入事件。最后nativeInit方法会调用reinterpret_cast方法,在C++中,reinterpret_cast 是一种类型转换操作符,用于在不同类型之间进行低级别的转换,它提供了一种不安全但非常强大的转换方式,通常用于将一个指针或引用类型转换为另一种类型,**这种转换只是将指针的表示重新解释为另一种类型,不改变指针所指向的内存内容。**这里将该对象指针返回给了java层, 这是为了打通java和native层,下次需要使用native层的NativeInputManager对象的时候,直接传递这个对象指针就可以访问了。

1.2.3 InputManager的构造方法

>frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {//注释1,创建inputDispatcher对象mDispatcher = createInputDispatcher(dispatcherPolicy);mClassifier = new InputClassifier(mDispatcher);//注释2,创建InputReader对象,其内部持有mDispatcher的引用mReader = createInputReader(readerPolicy, mClassifier);
}>frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) {//创建InputDispatcher对象return new android::inputdispatcher::InputDispatcher(policy);
}>frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) {//创建InputReader对象return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

在注释1处创建InputDispatcher类对象mDispatcher,然后在注释2处创建InputReader类对象mReader,其内部持有mDispatcher的引用,InputReader会不断循环读取EventHub中的原始输入事件,在对这些原始输入事件进行加工后交给InputDispatcher,由于WMS会将所有的窗口信息实时更新到InputDispatcher中,这样InputDispatcher就可以将输入事件派发给合适的窗口。

1.3 InputManagerService的启动

SystemServer的startOtherServices方法中关于IMS的最后一步是调用IMS的start方法。

1.3.1 InputManagerService的start方法

public class InputManagerService extends IInputManager.Stubimplements Watchdog.Monitor {private final long mPtr; private static native void nativeStart(long ptr);public void start() {Slog.i(TAG, "Starting input manager");nativeStart(mPtr);//注释1,调用nativeStart方法// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);//注释2,将自身添加到看门狗中进行监控...代码省略...}}

在注释1处调用nativeStart方法,此方法同样是一个Native方法,这里调用该方法的时候,还将前面在InputManagerService构造方法中创建的NativeInputManager对象指针传递了进来。在注释2处将自身添加到watchdog中进行监控,用户定时检测系统关键服务是否可能发生死锁。

1.3.2 nativeStart方法

>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);//注释1,获取NativeInputManager对象指针status_t result = im->getInputManager()->start();//注释2,调用InputManager的start方法if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}
// --- NativeInputManager ---
class NativeInputManager : public virtual RefBase,public virtual InputReaderPolicyInterface,public virtual InputDispatcherPolicyInterface,public virtual PointerControllerPolicyInterface {
public:NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper);inline sp<InputManagerInterface> getInputManager() const { return mInputManager; //注释3,返回对象实例 }
private:sp<InputManagerInterface> mInputManager;

在注释1处,将存储在Java层的NativeInputManager对象指针重新转化成Native层需要的对象指针,然后调用NativeInputManager的getInputManager方法获取其内部持有的InputManager对象,并调用InputManager对象的start方法。

1.3.3 InputManager的start方法

>frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {status_t result = mDispatcher->start();//注释1,调用inputdispatcher的start方法if (result) {ALOGE("Could not start InputDispatcher thread due to error %d.", result);return result;}result = mReader->start();//注释2,调用InputReader的start方法if (result) {ALOGE("Could not start InputReader due to error %d.", result);mDispatcher->stop();return result;}return OK;
}

在注释1处调用InputDispatcher的start方法,该方法内部会开启一个名为InputDispatcher的事件分发线程,在注释2处调用InputDispatcher的start方法,该方法内部会开启一个名为InputReader的事件读取线程。

1.3.3.1 Thread

在分析InputDispatcher和InputReader两个线程之前,我们需要先来看下Thread的run方法。

>system/core/libutils/Threads.cpp
status_t Thread::run(const char* name, int32_t priority, size_t stack)
{...代码省略...//调用createThreadEtc方法res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);...代码省略...
}int Thread::_threadLoop(void* user)Thread* const self = static_cast<Thread*>(user);...代码省略...do {bool result;result = self->threadLoop();...代码省略...if (result == false || self->mExitPending) {...代码省略...break;}...代码省略...} while(strong != 0);
}

threadLoop是一个方法指针,第二个参数是自己,最终会调用到_threadLoop方法并传入this指针,_threadLoop方法内部会调用self->threadLoop(),这个self就是当前Thread的this指针,除了result返回false或者调用了requestExit才会退出。 不然会一直循环调用self->threadLoop()函数,threadLoop在Thread中是一个纯虚函数,在其子类中实现,所以后面只要分析子线程的threadLoop方法即可。

1.3.3.2 InputDispatcher的start方法
>frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::start() {if (mThread) {return ALREADY_EXISTS;}//注释1,创建InputThread对象mThread = std::make_unique<InputThread>("InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });return OK;
}>frameworks/base/libs/androidfw/include/androidfw/Util.h
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}>frameworks/native/services/inputflinger/InputThread.cpp
class InputThreadImpl : public Thread {
public:explicit InputThreadImpl(std::function<void()> loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::function<void()> mThreadLoop;    
};
/**** @param name  线程的名称* @param loop  线程要执行的主循环函数或任务* @param wake 一个回调函数,用于唤醒或通知线程的动作*/
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake): mName(name), mThreadWake(wake) {mThread = new InputThreadImpl(loop);//注释2,创建InputThreadImpl对象,该对象用于处理线程的实际操作。mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}

在注释1处创建InputThread对象,分别传入了线程名称InputDispatcher、线程主循环函数dispatchOnce,回调函数mLooper->wake();然后在注释2处,也就是InputThread的构造方法中,创建InputThreadImpl对象,该对象继承自Thread ,是真正处理线程实际操作的对象。

1.3.3.3 InputDispatcher的dispatchOnce方法

在InputDispatcher线程启动之后,该线程循环执行的是dispatchOnce方法。

>frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();//注释1,检测缓存队列中是否有等待处理的命令if (!haveCommandsLocked()) {//将输入事件派发给合适的窗口dispatchOnceInnerLocked(&nextWakeupTime);}if (runCommandsLockedInterruptible()) {//记录线程下次被唤醒的时间nextWakeupTime = LONG_LONG_MIN;}//注释2,检查ANRconst nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);//注释3,处理空闲状态if (nextWakeupTime == LONG_LONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // release lock//注释4,计算InputDisPatcherThread需要休眠的最长时间nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}

在注释1处判断,如果没有待处理的命令,调用 dispatchOnceInnerLocked 方法执行一次调度循环;nextWakeupTime用于记录下次唤醒的时间,如果有待处理的命令,则将 nextWakeupTime 设置为 LONG_LONG_MIN,这表示需要立即处理下一个任务,因为命令的执行可能影响了调度的时机。在注释2处检查是否需要处理 ANR 事件,nextAnrCheck 记录了下一次检查 ANR 的时间,将 nextWakeupTime 更新为当前唤醒时间和 ANR 检查时间中的最小值,以确保在 ANR 检查之前唤醒。在注释3处,如果判断nextWakeupTime为LONG_LONG_MAX,表示没有命令、事件或待处理的任务,调度器将进入一个几乎无限长的睡眠状态,此时会通知其他线程或组件事件派发器已进入空闲状态。
在注释4处获取当前时间 currentTime,并使用toMillisecondTimeoutDelay方法计算直到 nextWakeupTime 的超时时间, 调用 mLooper->pollOnce(timeoutMillis) 方法,进入等待状态,直到有事件到达、超时或者有其他需要唤醒的情况发生。

1.3.3.4 InputReader的start和loopOnce方法

和InputDispatcher的start方法类似,InputReader的start方法也是进一步创建InputThread对象,分别传入了线程名称InputReader、线程主循环函数loopOnce,回调函数mEventHub->wake();该线程启动之后会循环执行loopOnce方法。

>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;
}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//注释1,获取设备节点的原始输入事件信息size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockstd::scoped_lock _l(mLock);//通知所有等待的线程,输入读取器仍然活跃。mReaderIsAliveCondition.notify_all();if (count) {//注释2,对mEventBuffer中的原始输入事件信息进行加工处理,加工后的输入事件会交给InputDispatcher来处理processEventsLocked(mEventBuffer, count);}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {//注释3,处理超时事件mNextTimeout = LLONG_MAX;timeoutExpiredLocked(now);}}if (oldGeneration != mGeneration) {inputDevicesChanged = true;inputDevices = getInputDevicesLocked();}} // release lock//注释4,如果输入设备状态发生了变化,调用mPolicy->notifyInputDevicesChanged方法进行事件通知if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}mQueuedListener->flush();//刷新或处理排队的监听器事件。
}

在注释1处从事件缓冲区中获取设备节点的原始输入事件信息并将其存放到mEventBuffer中,timeoutMillis 是等待事件的超时时间,mEventBuffer 是存放事件的缓冲区,EVENT_BUFFER_SIZE 是缓冲区的大小,事件信息主要有两种,设备节点的增删事件、原始输入事件;在注释2处 判断是否有事件,如果有则调用processEventsLocked方法,该方法会对mEventBuffer中的原始输入事件信息进行加工处理,并将加工后的输入事件会交给InputDispatcher处理;在注释3处判断输入事件是否已经超时,如果超时则调用timeoutExpiredLocked方法处理超时事件;在注释4处判断输入设备状态是否发生了变化,如果是则通知相关的策略或组件。

二、IMS架构图

这里我们结合InputManagerService的构造方法和start方法画了个简单的IMS架构图。
IMS架构图

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Kubernetes存储Volume
  • 【STM32】时钟体系
  • 凌鸥学园电机控制学习盛宴,诚邀您的加入
  • 若依后端添加子模块swagger分区
  • MySQL中的事物详解
  • Electron程序逆向(asar归档解包)
  • YoloV8实战:使用YoloV8实现OBB框检测
  • 数据结构---单链表(常见的复杂操作)
  • OpenAI 神秘模型「草莓」预计今秋推出,ChatGPT 将迎重大升级|TodayAI
  • Flutter 自动化测试 -appium-flutter-driver
  • git clone 别人的项目上传到自己的Gitee或者github仓库
  • 小白指南:Linux怎么创建压缩包?又怎么解压缩?
  • 让甲方看得见服务器资源降本增效-软件开发不仅考虑开发成本也要重视长期的运维成本
  • Java基础(4)- IDEA
  • 嵌入式软件开发之状态机与事件驱动分析
  • 《深入 React 技术栈》
  • 【知识碎片】第三方登录弹窗效果
  • ECS应用管理最佳实践
  • js对象的深浅拷贝
  • Linux gpio口使用方法
  • MySQL数据库运维之数据恢复
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • passportjs 源码分析
  • Python爬虫--- 1.3 BS4库的解析器
  • Redux 中间件分析
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 排序算法之--选择排序
  • 前嗅ForeSpider教程:创建模板
  • 十年未变!安全,谁之责?(下)
  • 实战|智能家居行业移动应用性能分析
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 用简单代码看卷积组块发展
  • 由插件封装引出的一丢丢思考
  • 白色的风信子
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • ###STL(标准模板库)
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (10)ATF MMU转换表
  • (11)MATLAB PCA+SVM 人脸识别
  • (zhuan) 一些RL的文献(及笔记)
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (九十四)函数和二维数组
  • (十五)使用Nexus创建Maven私服
  • (一) 初入MySQL 【认识和部署】
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测