1 前言

​ IMS 是 InputManagerService 的简称,主要负责输入事件管理。

1.1 基本概念

  • 输入设备:屏幕、电源/音量、键鼠、充电口、蓝牙、wifi 等
  • 设备节点:当输入设备可用时,Linux 内核会在 /dev/input 中创建对应的设备节点
  • 输入事件:触摸事件、按键事件、鼠标事件、插拔事件等
  • 输入子系统:负责采集 Linux 内核中输入事件原始信息,原始信息由 Kernel space 的驱动层一直传递到 User space 的设备节点

​ IMS 所做的工作就是监听 /dev/input 下的所有的设备节点,当设备节点有数据时,对数据进行加工处理并找到合适的 Window,将输入事件派发给它。

1.2 IMS 框架

(1)EventHub

  • 监听、扫描、打开 /dev/input 目录下的输入设备
  • 根据配置文件和键值映射文件对设备进行配置和键值映射
  • 将所有设备添加到本地列表中
  • 抽取驱动程序上报的 inputEvent,主要包含设备可用性变化事件(设备事件)、设备节点中读取的原始输入事件。

(2)InputReader

  • 根据设备类型添加不同 InputMapper
  • 通过 EventHub.getEvents() 获取 EventHub 中未处理的事件

(3)InputDispatcher

  • 事件过滤,并发给上层。

2 Java 层 IMS 初始化流程

2.1 IMS 启动流程

(1)main

​ /frameworks/base/services/java/com/android/server/SystemServer.java

public static void main(String[] args) {
new SystemServer().run();
}

(2)run

​ /frameworks/base/services/java/com/android/server/SystemServer.java

private void run() {
try {
...
// 创建Looper
Looper.prepareMainLooper();
// 加载libandroid_servers.so
System.loadLibrary("android_servers");
// 创建系统的 Context:ContextImpl.createSystemContext(new ActivityThread())
createSystemContext();
// 创建 SystemServiceManager
mSystemServiceManager = new SystemServiceManager(mSystemContext);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
...
}
...
try {
//启动引导服务,ActivityManagerService、ActivityTaskManagerService、PackageManagerService、PowerManagerService、DisplayManagerService 等
startBootstrapServices();
//启动核心服务,BatteryService、UsageStatusService 等
startCoreServices();
//启动其他服务,InputManagerService、WindowManagerService、CameraService、AlarmManagerService 等
startOtherServices();
...
}
...
// 开启消息循环
Looper.loop();
}

(3)startOtherServices

​ /frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices() {
...
WindowManagerService wm = null;
...
InputManagerService inputManager = null;
...
try {
...
inputManager = new InputManagerService(context);
...
//PhoneWindowManager 是 WMP 的实现类
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager, false, DUMP_FLAG_PRIORITY_CRITICAL);
...
mActivityManagerService.setWindowManager(wm);
...
wm.onInitReady(); //initPolicy
...
//wm 的 mInputManagerCallback 属性在定义时就被初始化
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
inputManager.start();
...
}
}

​ 通过 ServiceManager.addService() 将 Context.INPUT_SERVICE 与 IMS 绑定,因此在其他进程中可以通过如下方式获取 IMS。

IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
IInputManager im = IInputManager.Stub.asInterface(b);

2.2 IMS 初始化

(1)构造方法

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

public InputManagerService(Context context) {
this.mContext = context;
//在 android.display 线程中处理消息
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
...
//mPtr 为 JNI 层 NativeInputManager 对象的地址
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
String doubleTouchGestureEnablePath = context.getResources().getString(R.string.config_doubleTouchGestureEnableFile);
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null : new File(doubleTouchGestureEnablePath);
//添加本地服务
LocalServices.addService(InputManagerInternal.class, new LocalService());
}

​ nativeInit() 方法在 JNI 层创建了 NativeInputManager 对象,并返回其地址(后文会介绍);LocalService 是 IMS 的内部类,也是 InputManagerInternal 的实现类。

(2)setWindowManagerCallbacks

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

public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
mWindowManagerCallbacks = callbacks;
}

​ callbacks 来自 WMS.mInputManagerCallback,属于 InputManagerCallback 类(IMS.WindowManagerCallbacks 的实现类),WMS.mInputManagerCallback 在定义时就被初始化。

(3)start

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

public void start() {
//启动 Native 层的 InputReaderThread 和 InputDispatcherThread 线程
nativeStart(mPtr);
//注册监听器
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver(); mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}

​ nativeStart() 方法启动 Native 层 InputReaderThread 和 InputDispatcherThread 线程(后面会介绍)。

3 Native 层 IMS 初始化流程

​ 本节主要介绍 IMS 构造方法中调用 nativeInit() 方法、start() 方法中调用 nativeStart() 方法的后续流程。

​ 如图,展示了 IMS 初始化流程,浅蓝色、绿色、紫色分别表示 Java 层、JNI 层、Native 层。

3.1 nativeInit 后续流程

(1)gInputManagerMethods

​ /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static const JNINativeMethod gInputManagerMethods[] = {
//Java 层调用的 nativeInit 方法对应 JNI 层的 nativeInit 方法
{ "nativeInit",
"(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J",
(void*) nativeInit },
//Java 层调用的 nativeStart 方法对应 JNI 层的 nativeStart 方法
{ "nativeStart", "(J)V",
(void*) nativeStart },
...
}

​ gInputManagerMethods 数组列出了 Java 层调用 nativeInit()、nativeStart() 方法到 JNI 层具体调用方法的映射关系。

(2)nativeInit

​ /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

//Java 层调用:mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
static jlong nativeInit(JNIEnv* env, jclass, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
//获取 JNI 层消息队列
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
...
//创建 NativeInputManager
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper());
im->incStrong(0);
//返回 NativeInputManager 对象指针给 Java 层(mPtr)
return reinterpret_cast<jlong>(im);
}

​ NativeInputManager 是在该文件中定义的类。

(3)NativeInputManager

​ /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

#include <inputflinger/InputManager.h>

NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
JNIEnv* env = jniEnv();
mServiceObj = env->NewGlobalRef(serviceObj);
...
mInteractive = true;
//创建 Native 层 InputManager,2 个 this 分别为:InputReaderPolicyInterface、InputDispatcherPolicyInterface
mInputManager = new InputManager(this, this);
defaultServiceManager()->addService(String16("inputflinger"), mInputManager, false);
}

​ NativeInputManager 实现了 RefBase、InputReaderPolicyInterface、InputDispatcherPolicyInterface、PointerControllerPolicyInterface 4个接口。InputManager.h 中定义了抽象的 InputManager 类,InputManager.cpp 实现了 InputManager.h 中的抽象方法。

(4)InputManager

​ /frameworks/native/services/inputflinger/InputManager.h

class InputManager : public InputManagerInterface, public BnInputFlinger {
...
public:
InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
virtual status_t start();
...
virtual void setInputWindows(const std::vector<InputWindowInfo>& handles, const sp<ISetInputWindowsListener>& setInputWindowsListener);
virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
...
virtual void registerInputChannel(const sp<InputChannel>& channel);
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
sp<InputClassifierInterface> mClassifier;
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
void initialize();
};

​ 注意:InputManager 继承了 BnInputFlinger 接口(Binder 跨进程通讯的服务端)。

(5)InputManager

​ /frameworks/native/services/inputflinger/InputManager.cpp

#include "InputManager.h"
#include "InputReaderFactory.h" InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
//创建 InputDispatcher
mDispatcher = new InputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
//创建 InputReader,createInputReader() 是 InputReaderFactory 类的方法
mReader = createInputReader(readerPolicy, mClassifier);
initialize();
}

(6)InputDispatcher

​ /frameworks/native/services/inputflinger/InputDispatcher.cpp

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy), ... {
//创建消息循环
mLooper = new Looper(false);
mReporter = createInputReporter();
...
policy->getDispatcherConfiguration(&mConfig);
}

(7)createInputReader

​ /frameworks/native/services/inputflinger/InputReaderFactory.cpp

sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) {
return new InputReader(new EventHub(), policy, listener);
}

​ 注意:此处创建了 EventHub,并注入到 InputReader 中。

(8)InputReader

​ /frameworks/native/services/inputflinger/InputReader.cpp

InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy), ... {
mQueuedListener = new QueuedInputListener(listener);
...
}

(9)EventHub

​ /frameworks/native/services/inputflinger/EventHub.cpp

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) {
...
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
...
mINotifyFd = inotify_init();
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
...
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
...
int wakeFds[2];
result = pipe(wakeFds);
...
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
...
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
...
eventItem.data.fd = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
...
}

(10)initialize

​ /frameworks/native/services/inputflinger/InputManager.cpp

void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

​ InputReaderThread 和 InputDispatcherThread 都继承了 Thread 类。

​ 在 InputManager.h 文件中,已申明 InputReaderThread 和 InputDispatcherThread 的源码不再开源,如下。

/*
* By design, the InputReaderThread class and InputDispatcherThread class do not share any
*/

3.2 nativeStart 后续流程

(1)nativeStart

​ /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static void nativeStart(JNIEnv* env, jclass, jlong ptr) {
//通过对象地址获取 NativeInputManager 对象指针
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
//调用 Native 层 InputManager 的 start() 方法
status_t result = im->getInputManager()->start();
...
} inline sp<InputManager> getInputManager() const {
return mInputManager;
}

(2)start

​ /frameworks/native/services/inputflinger/InputManager.cpp

status_t InputManager::start() {
//开启 DispatcherThread 线程
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
...
//开启 ReaderThread 线程
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
...
return OK;
}

​ 声明:本文转自【framework】IMS启动流程

【framework】IMS启动流程的更多相关文章

  1. go web framework gin 启动流程分析

    最主要的package : gin 最主要的struct: Engine Engine 是整个framework的实例,它包含了muxer, middleware, configuration set ...

  2. Phalcon Framework的MVC结构及启动流程分析

    目前的项目中选择了Phalcon Framework作为未来一段时间的核心框架.技术选型的原因会单开一篇Blog另说,本次优先对Phalcon的MVC架构与启动流程进行分析说明,如有遗漏还望指出. P ...

  3. Phalcon Framework的Mvc结构及启动流程(部分源码分析)

    创建项目 Phalcon环境配置安装后,可以通过命令行生成一个标准的Phalcon多模块应用 phalcon project eva --type modules入口文件为public/index.p ...

  4. Android进阶系列之源码分析Activity的启动流程

    美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...

  5. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  6. 老李推荐:第5章2节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 启动流程概览

    老李推荐:第5章2节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 启动流程概览   每个应用都会有一个入口方法来供操作系统调用执行,Monkey这个应用的入口方法就 ...

  7. 海思uboot启动流程详细分析(三)【转】

    1. 前言 书接上文(u-boot启动流程分析(二)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_init_r所代表的. ...

  8. Android N 的开机启动流程概述

    原地址:https://blog.csdn.net/h655370/article/details/77727554 图片展示了Android的五层架构,从上到下依次是:应用层,应用框架层,库层,运行 ...

  9. Phalcon的Mvc结构及启动流程(部分源码分析)

    Phalcon本身有支持创建多种形式的Web应用项目以应对不同场景,包括迷你应用.单模块标准应用.以及较复杂的多模块应用 创建项目 Phalcon环境配置安装后,可以通过命令行生成一个标准的Phalc ...

  10. Android解析ActivityManagerService(一)AMS启动流程和AMS家族

    前言 此前在Android系统启动流程.应用进程以及深入四大组件这三个系列文章中,都提及到了AMS,但都没有系统的来讲解它,本文就以AMS为主来进行讲解,其中会有一些知识点与这些系列文章有所重合,这里 ...

随机推荐

  1. [转帖]Postmark - 存储性能测试工具

    1. 引言 Postmark是由著名的NAS提供商NetApp开发,用来测试其产品的后端存储性能. Postmark主要用于测试文件系统在邮件系统或电子商务系统中性能,这类应用的特点是:需要频繁.大量 ...

  2. 【转帖】You can now run a GPT-3-level AI model on your laptop, phone, and Raspberry Pi

    https://arstechnica.com/information-technology/2023/03/you-can-now-run-a-gpt-3-level-ai-model-on-you ...

  3. Linux运行服务的几种方式

    摘要 1. nohup & 2. screen 3. bg & disown 4. systemd 5. crontab @reboot 背景 最近一直在用linux 想着多总结一下. ...

  4. JDK发布版本的总结

    https://www.oracle.com/java/technologies/javase/8all-relnotes.html 从官网总结一下每个版本的发布日期 Java SE 8u141 Ad ...

  5. 文心一言 VS 讯飞星火 VS chatgpt (186)-- 算法导论14.1 3题

    三.用go语言,写出OS-SELECT的非递归版本. 文心一言: 下面是使用Go语言编写的OS-SELECT的非递归版本: func OS_SELECT(T *Node, k int) *Node { ...

  6. 据说这道Go面试题90%的人都搞错了!

    [Go面试向]defer与time.sleep初探 大家好,我是阳哥,这是我们Go就业训练营小伙伴 寸铁同学 整理的一道很有意思的面试题. 知其然更要知其所以然,通过断点调试的思路带你搞清楚来龙去脉. ...

  7. 【JS 逆向百例】某易支付密码 MD5+AES 加密分析

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  8. 搭建mongo的replica set

    搭建mongo的replica set 前言 安装 构建副本集 加入认证 备份数据 备份数据到本地 数据恢复 搭建mongo的replica set 前言 准备三台机器,相互可以访问的.处理思路,先构 ...

  9. SqlSugar联表查询

    Join用法 语法糖1.2和3 在Where OrderBy GroupBy Select用法都一样的,他们区别就在JOIN的方式不一样,其它都一样 语法糖1 优点:好理解,5个表以内的联表非常爽,支 ...

  10. 19.8 Boost Asio 异或加密传输

    异或加密是一种对称加密算法,通常用于加密二进制数据.异或操作的本质是对两个二进制数字进行比较,如果它们相同则返回0,如果不同则返回1.异或加密使用一把密钥将明文与密文进行异或运算,从而产生密文.同时, ...