深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动
文章目录
- 前言
- 一、输入系统的基本组成部分
- 二、输入系统相关源码分析
- 1、IMS 构建
- 1.1、SystemServer # startOtherServices()
- 1.2、InputManagerService
- 1.3、NativeInputManager # nativeInit()
- 1.4、NativeInputManager
- 1.5、InputManager
- 1.6、InputDispatcher
- 1.7、InputReader
- 1.8、EventHub
- 1.9、小结
- 2、IMS 启动
- 2.1、IMS # start()
- 2.2、NativeInputManager # nativeStart()
- 2.3、InputManager # start()
- 2.4、InputDispatcher # start()
- 2.5、InputReader # start()
- 2.6、InputThread
- 3、IMS 系统就绪
- 三、总结
- 1、IMS 启动时序图
- 2、IMS 成员关系图
- 参考
前言
Android 输入系统(Input System) 的工作原理,包括:输入设备的管理、输入事件的加工方式及派发流程。首先输入设备包括:触摸屏,键盘,鼠标和手柄等,其中触摸屏与键盘是 Android 最普遍也是最标准的输入设备。当用户操作输入设备时,Linux 内核接收到相应的硬件中断,然后将中断加工成原始的输入事件数据并写入其对应的设备节点中,在用户空间可以通过输入系统内部的读取函数将原始事件数据读出,并进行一系列的翻译加工成 Android 输入事件,然后在所有的窗口中寻找合适的事件接收者,并派发给它来消费该输入事件。可见,输入系统在整个输入事件处理过程中起到了承上启下的衔接作用。
一、输入系统的基本组成部分
上图展示了输入事件的处理流程以及输入系统中最基本的参与者,下面简要介绍一下各个参与者:
- Linux 内核:接受输入设备的中断,并将原始输入事件的数据写入设备节点中;
- 设备节点:内核与 InputManagerService 的桥梁,它将原始事件的数据暴露给用户空间,以便 InputManagerService 可以从中读取事件;
- InputManagerService:Android 系统服务,以后简称 IMS,其分为 Java 层和 Native 层两部分。Java 层负责与 WindowManagerService 通信。而 Native 层则是InputReader 和 InputDispatcher 两个输入系统关键组件的运行容器;
- EventHub:直接访问所有的设备节点。并且正如其名字所描述的,它通过一个名为 getEvents( ) 的函数将所有输入系统相关的待处理的底层事件返回给使用者,包括原始输入事件、设备节点的增删等。
- InputReader:IMS 中的关键组件之一,运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。通过其线程循环不断地通过 getEvent( ) 函数从 EventHub 中将事件取出并进行处理。对于设备节点的增删事件,将会更新输入设备列表与配置。对于原始输入事件,InputReader 对其进行翻译、组装、封装为包含更多信息、更具可读性的输入事件,然后交给 InputDispatcher 进行派发;
- InputReaderPolicy:为 InputReader 的事件加工处理提供一些策略配置,例如键盘布局信息等;
- InputDispatcher:IMS 中的另一个关键组件,也运行于一个独立的线程中。InputDispatcher 中保管了来自 WindowManagerService 的所有窗口的信息,其收到来自 InputReader 的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口;
- InputDispatcherPolicy:为 InputDispatcher 的派发过程提供策略控制。例如截取某些特定的输入事件用作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是 HOME 键被 InputDispatcherPolicy 截取到 PhoneWindowManager 中进行处理,并阻止窗口收到 HOME 键按下的事件;
- WindowManagerService:虽不是输入系统中的成员,但却对 InputDispatcher 的正常工作起到了至关重要的作用。当新建窗口时,WMS 为新窗口和 IMS 之间创建了事件传递所用的通道。另外,WMS 还将所有窗口的信息,包括窗口的可点击区域、焦点窗口等信息,实时地更新到 IMS 的 InputDispatcher 中,使得 InputDispatcher 可以正确地将事件派发到指定的窗口;
- ViewRootImpl:对某些窗口,如壁纸窗口、SurfaceView 的窗口来说,窗口就是输入事件派发的终点。而对其他的如 Activity、对话框等使用了 Android 控件系统的窗口来说,输入事件的终点是控件 View。ViewRootImpl 将窗口所接收的输入事件沿着控件树将事件派发给感兴趣的控件 View;
二、输入系统相关源码分析
我们知道,Zygote 进程创建并启动后,在 fork 出的子进程 SystemServer 的初始化过程中启动 Android 系统所有的 Service 服务,这些系统服务分为三大类:引导服务、核心服务及其他服务,具体的启动流程可参考探索Framework之SystemServer进程的启动详解。
输入系统服务 IMS 是在启动其他服务里面启动的,接下来从源码的角度来继续探索分析。
1、IMS 构建
在 SystemServer 类中找到启动其他服务 startOtherServices() 方法的代码,提取主要的逻辑代码进行分析,源码如下:
1.1、SystemServer # startOtherServices()
xref: /frameworks/base/services/java/com/android/server/SystemServer.java
public final class SystemServer implements Dumpable {
......
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
final Context context = mSystemContext;
WindowManagerService wm = null;
......
InputManagerService inputManager = null;
......
try {
......// 启动 InputManagerService 服务
t.traceBegin("StartInputManagerService");
// 新建 InputManagerService 对象
inputManager = new InputManagerService(context);
......
t.traceBegin("StartWindowManagerService"); // 启动 WindowManagerService 服务
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
// 使用新建的 IMS 对象来构建 WMS 对象
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
......// 将 InputManagerService 发布到 ServiceManager 以便调用者可以访问 IMS 提供的接口
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
t.traceBegin("SetWindowManagerService"); // ActivityManagerService 设置 WindowManagerService
mActivityManagerService.setWindowManager(wm);
t.traceBegin("StartInputManager"); // 设置向 WMS 发起回调的 callback 对象
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
inputManager.start(); // 启动 InputManagerService,具体见
......
} catch (Throwable e) {
......// 日志输出并抛出异常
}
......
final InputManagerService inputManagerF = inputManager;
t.traceBegin("MakeInputManagerServiceReady");
try {
if (inputManagerF != null) {
// 输入系统 IMS 准备就绪
inputManagerF.systemRunning();
}
} catch (Throwable e) {
reportWtf("Notifying InputManagerService running", e);
}
......
}
......
}
IMS 的启动流程可以分为以下三个步骤:
- 构建 IMS 实例对象,并建立上层与底层的映射关系。
- 启动 IMS,其内部就是启动 native 层输入系统的几个重要参与者(后续会分析)。
- IMS 系统就绪,此时 Java 层会同步一些配置给 native 层输入系统。
首先是 IMS 实例对象的构建,分析查看 IMS 类的源码
1.2、InputManagerService
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
......
private final Context mContext;
private final InputManagerHandler mHandler;
......
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
......
public InputManagerService(Context context) {
this.mContext = context;
// 获取 DisplayThread 的 Looper 创建 IMS 内部的 InputManagerHandler 对象
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mStaticAssociations = loadStaticInputPortAssociations();
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
// 每一个分为 Java 和 Native 两部分的对象在创建时都会有一个 native 函数
// 在创建 Java 层对象的同时 native 层也创建一个,注意:使用的是同一个 Looper 对象
// mPtr 指向底层创建的 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);
// 新建 IMS 的本地系统服务 LocalService,其继承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
}
方法中,获取 DisplayThread 的 Looper,新建 InputManagerHandler 对象,然后调用 native 层的 nativeInit() 函数,创建NativeInputManager 对象,最后新建 IMS 的本地系统服务 LocalService,其继承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中。
DisplayThread 在 system_server 进程中是单例的,且只能被 WindowManager、DisplayManager、InputManager 使用。
1.3、NativeInputManager # nativeInit()
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = { // JNI 注册的映射关系
/* name, signature, funcPtr */
{"nativeInit",
"(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/"
"MessageQueue;)J",
(void*)nativeInit},
};
static jlong nativeInit(JNIEnv*env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
// 由传入的 Java 层的 MessageQueue 转换获取 native 层的 MessageQueue
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
// 新建 NativeInputManager 对象,此对象将是 native 层组件与 Java 层 IMS 进行通信的桥梁
NativeInputManager * im = new NativeInputManager(contextObj, serviceObj,
messageQueue -> getLooper());
im -> incStrong(0);
// 返回指向 NativeInputManager 对象的指针给 Java 层的 IMS,IMS 将其保存在 mPtr 成员变量中
return reinterpret_cast < jlong > (im);
}
通过 JNI 注册的映射关系,找到 native 层的 nativeInit() 函数,首先由传入的 Java 层的 MessageQueue 转换获取 native 层的 NativeMessageQueue 对象,然后新建 NativeInputManager 对象。
1.4、NativeInputManager
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
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; }
private:
sp<InputManagerInterface> mInputManager;
jobject mServiceObj; // IMS 对象
sp<Looper> mLooper; // Looper 对象
Mutex mLock;
struct Locked {
......例如,mLocked.showTouches 是
// 如果为 True,则启用指针手势
bool pointerGesturesEnabled;
// 由开发者选项中 "Show taps" 决定的,其功能是在屏幕上显示一个触摸点
bool showTouches;
......
} mLocked GUARDED_BY(mLock);
std::atomic<bool> mInteractive;
......
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
};
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>&looper) :
mLooper(looper),mInteractive(true) {
JNIEnv * env = jniEnv();
// 保存 Java 层的 InputManagerService 对象
mServiceObj = env -> NewGlobalRef(serviceObj);
{ // mLocked 的类型是 struct Locked,这里初始化了一些参数,这些参数会被 Java 层改变
AutoMutex _l (mLock);
mLocked.systemUiLightsOut = false;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
mLocked.pointerCapture = false;
mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
// 创建了 native 层的 InputManager,它才是底层输入系统的服务
InputManager * im = new InputManager(this, this);
mInputManager = im;
// 将 InputManager 注册到 ServiceManager 中
defaultServiceManager()->addService(String16("inputflinger"), im);
}
- 在 NativeInputManager 的构造函数中,创建一个全局引用,并通过 mServiceObj 指向 Java 层的 IMS 对象,便于后续可以通过 mServiceObj 调用 Java 层 IMS 对象的方法。
- 初始化参数,这里要注意一个结构体变量 mLocked,它的一些参数都是由 Java 层控制的。
- 然后将自己作为参数来新建 InputManager 对象,并将 InputManager 注册到 ServiceManager 中,InputManager 才是 native 层输入系统的服务。
注意:由 NativeInputManager 类的声明可以看到,其实现了 InputReaderPolicyInterface 与 InputDispatcherPolicyInterface 两个接口。
1.5、InputManager
xref: /frameworks/native/services/inputflinger/InputManager.h
class InputManager : public InputManagerInterface, public BnInputFlinger {
protected:
~InputManager() override;
public:
InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
......
private:
sp<InputReaderInterface> mReader;
sp<InputClassifierInterface> mClassifier;
sp<InputDispatcherInterface> mDispatcher;
};
xref: /frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 创建 InputDispatcher 对象,使用 InputDispatcherPolicyInterface 接口,用于对事件进行分发
mDispatcher = createInputDispatcher(dispatcherPolicy);
// 创建 InputClassifier 对象,使用 InputListenerInterface,用于对事件分类
mClassifier = new InputClassifier(mDispatcher);
// 创建 InputReader 对象,使用 InputReaderPolicyInterface 和 InputListenerInterface
// 其通过 EventHub 监听"/dev/input"事件,获取事件,然后把事件加工后,发送给 InputClassfier
mReader = createInputReader(readerPolicy, mClassifier);
}
在 InputManager 内部创建了三个子模块:InputReader、InputClassifier、InputDispatcher,其作用如下:
- InputReader:负责从 EventHub 中获取事件,然后把事件加工后,发送给 InputClassfier。
- InputClassifer:负责把事件发送给 InputDispatcher,但是它会对触摸事件进行一个分类工作。
- InputDispatcher:对进行事件分发。
此外,在上一小节的分析中,我们知道在构建 InputManager 实例对象时使用了两个 this 参数,而 InputManager 构造函数需要的两个接口参数正是由 NativeInputManager 实现的,而具体使用这两个接口的不是 InputManager 自身,而是它内部的子模块 InputDispatcher 和 InputReader。
InputDispatcher 和 InputReader 在构建时都传递了 NativeInputManager 对象参数,并赋值到各自的 mPolicy 变量,后续可直接通过 mPolicy 调用 Java 层 IMS 对象方法,因此 InputManager 向 Java 层通信的能力是由子模块 InputDispatcher 和 InputReader 实现的。
接下来首先来看看 InputDispatcher 是如何通过 createInputDispatcher() 函数创建的,详见接下来两节的源码分析。
1.6、InputDispatcher
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
sp<InputDispatcherInterface> createInputDispatcher(
const sp<InputDispatcherPolicyInterface>& policy) {
return new android::inputdispatcher::InputDispatcher(policy);
}
方法很简单,内部直接新建 InputDispatcher 对象,再继续查看 InputDispatcher 的构造函数:
xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h
class InputDispatcher : public android::InputDispatcherInterface {
protected:
~InputDispatcher() override;
public:
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
......
private:
std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
sp<Looper> mLooper;
sp<InputReporterInterface> mReporter;
};
xref: /frameworks/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),
mFocusedWindowRequestedPointerCapture(false),
mWindowTokenWithPointerCapture(nullptr),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator),
mCompatService(getCompatService()) {
mLooper = new Looper(false); // 新建自己的 Looper 对象
mReporter = createInputReporter(); // 新建 InputReporter 对象
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
在调用 InputDispatcher 的构造函数构建实例对象的同时将入参 policy 赋值给 mPolicy 进行保存 (这里入参 policy 即是 NativeInputManager 对象)。其次新建自己的 Looper 对象,然后使用类似创建 InputDispatcher 的 createInputReporter() 函数新建 InputReporter 对象,代码比较简单,不再深入追踪,感兴趣的可自行查看。
1.7、InputReader
接着来看看 InputReader 是如何通过 createInputReader() 函数创建的,一起跟着源码来学习。
xref: /frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) {
// 创建 EventHub 对象传入到 InputReader 的构造函数中来新建 InputReader 对象
return new InputReader(std::make_unique<EventHub>(), policy, listener);
}
该方法里面,在新建 InputReader 对象时,结合 InputReader 类的构造函数可知,第一个参数是 EventHub 的实例对象,那么 EventHub 对象是怎么创建的呢?
这里需要知道一些 C++ 有关的知识,std::make_unique 的语法如下:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
std::make_unique:是 C++11 标准引入的一个模版函数,用于动态分配指定类型的内存,并返回一个指向分配内存的唯一指针 (即 std::unique_ptr)。语法中,T 是指定的类型,Args 是可变长模板参数包,用于传递给指定类型的构造函数的参数。在调用 std::make_unique 时,通过 Args 包传入构造函数的参数会被转发给类型 T 的构造函数,以生成相应的对象实例。该函数返回的指针是一个 std::unique_ptr 类型,表示一个拥有指向动态内存的所有权的对象。
xref: /frameworks/native/services/inputflinger/reader/include/InputReader.h
class InputReader : public InputReaderInterface {
public:
InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener);
virtual ~InputReader();
protected:
// 在循环过程的每次迭代中,InputReader 读取并处理一条来自 EventHub 的传入消息
void loopOnce();
private:
std::unique_ptr<InputThread> mThread;
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
sp<QueuedInputListener> mQueuedListener;
};
xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
: mContext(this),
mEventHub(eventHub),
mPolicy(policy),
mGlobalMetaState(0),
mLedMetaState(AMETA_NUM_LOCK_ON),
mGeneration(1),
mNextInputDeviceId(END_RESERVED_ID),
mDisableVirtualKeysTimeout(LLONG_MIN),
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
std::scoped_lock _l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
与构建 InputDispatcher 对象类似,在调用 InputReader 的构造函数构建实例对象的同时将入参 policy 赋值给 mPolicy,eventHub 对象赋值给 mEventHub 保存,同时新建 QueuedInputListener 监听对象。
通过前一小节的分析可知,EventHub 实例对象是通过调用 std::make_unique() 函数来创建的,那接下来一起去看看 EventHub 具体都做了些什么?
1.8、EventHub
xref: /frameworks/native/services/inputflinger/reader/include/EventHub.h
class EventHub : public EventHubInterface {
public:
EventHub();
private:
int32_t mNextDeviceId;
BitSet32 mControllerNumbers;
std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices;
std::vector<std::unique_ptr<Device>> mOpeningDevices;
std::vector<std::unique_ptr<Device>> mClosingDevices;
int mEpollFd;
int mINotifyFd;
int mWakeReadPipeFd;
int mWakeWritePipeFd;
int mInputWd;
int mVideoWd;
// 一次最多可处理的信号fd的数量
static const int EPOLL_MAX_EVENTS = 16;
// 挂起的 epoll 事件数组和下一个要处理的事件的索引
struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];
size_t mPendingEventCount;
size_t mPendingEventIndex;
bool mPendingINotify;
};
xref: /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();
// 创建 Epoll 对象,mEpollFd 为 Epoll 对象的描述符
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
mINotifyFd = inotify_init(); // 创建并初始化 INotify 对象
// DEVICE_PATH值为"/dev/input",监听该目录下的设备节点创建与删除操作,然后通过 read 函数读取事件
mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
......
struct epoll_event eventItem = {};
eventItem.events = EPOLLIN | EPOLLWAKEUP;
eventItem.data.fd = mINotifyFd;
//epoll_ctl 将事件监听添加到 Epoll 对象中,参数值 EPOLL_CTL_ADD:表示增加事件
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);
......
}
EventHub 的构造函数主要工作有:
- 新建并初始化 Epoll、INotify 对象等。
- 调用 inotify_add_watch 函数,监听 “/dev/input” 目录下的设备节点创建与删除操作,然后通过 read() 函数读取事件。
- 将 INotify 添加到 Epoll 中,作为一个监控对象。
- 创建管道,将管道读取端的可读事件添加到 Epoll 中,使 epoll_wait() 函数返回,唤醒 InputReader 线程。
至此,IMS 在 Java 层和 native 层的实例对象都已创建完成,并且在这个过程中,输入系统的重要参与者也均创建完成。
1.9、小结
Java 层的 IMS 的主要工作是为 ReaderPolicy 与 DispatcherPolicy 提供实现,以及与 Android 其他系统服务进行协作,其中最主要的协作者是 WMS。
NativeInputManager 位于 IMS 的 JNI 层,负责 Native 层的组件与 Java 层的 IMS 之间的相互通信。同时为 InputReader 及 InputDispatcher 提供了策略请求的接口。策略请求被他转发给 Java 层的 IMS,由 IMS 进行最终的决策定夺。
InputManager 是 InputReader 与 InputDispatcher 的运行容器,在启动 InputReader 与 InputDispatcher 时,分别新建自己的运行线程 InputThreadImpl 并启动运行。
2、IMS 启动
在上一节的 SystemServer # startOtherServices() 方法中,在构建完 IMS 后,IMS 系统中的各个重要参与者仍处于待命状态,需调用 IMS # start() 函数来启动 IMS,继续追踪源码分析:
2.1、IMS # start()
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
......
private static native void nativeStart(long ptr);
......
public void start() {
Slog.i(TAG, "Starting input manager");
// 启动 native 层的 IMS
nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);
// 监听Settings.System.POINTER_SPEED,这个表示手指的速度
registerPointerSpeedSettingObserver();
// 监听Settings.System.SHOW_TOUCHES,这个表示是否在屏幕上显示触摸坐标
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
registerLongPressTimeoutObserver();
registerMaximumObscuringOpacityForTouchSettingObserver();
registerBlockUntrustedTouchesModeSettingObserver();
// 监听用户切换
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("user switched");
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
// 从数据库获取值,并传递给 native 层
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
updateDeepPressStatusFromSettings("just booted");
updateMaximumObscuringOpacityForTouchFromSettings();
updateBlockUntrustedTouchesModeFromSettings();
}
......
}
IMS 的启动过程如下:
- 启动 native 层输入系统,其实就是启动刚刚说到的 InputReader 和 InputDispatcher。
- 注册监听广播,因为这些广播与输入系统的配置有关,当接收到这些广播,会更新配置到 native 层。
- 直接读取配置,更新到 native 层输入系统。
2.2、NativeInputManager # nativeStart()
xref: /frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static const JNINativeMethod gInputManagerMethods[] = { // JNI 注册的映射关系
/* name, signature, funcPtr */
{"nativeStart", "(J)V", (void*)nativeStart},
};
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
// 将 Java 层保存的 NativeInputManager 对象的指针转换成 NativeInputManager 对象
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
// 查看 1.4 NativeInputManager 的源码可知,获取到 InputManager 对象,然后调用其 start() 函数
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
首先将 Java 层保存 NativeInputManager 对象的指针 mPtr 转换成 NativeInputManager 对象,然后调用 NativeInputManager # getInputManager() 函数获取到 InputManager 对象,接着调用 InputManager # start() 函数继续启动流程。
reinterpret_cast 的功能可以分为两类:1、指针和整数之间的转换;2、不同类型的指针/成员指针/引用之间的转换。
2.3、InputManager # start()
xref: /frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
// 启动承载 InputDispatcher 的线程
status_t result = mDispatcher->start();
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
// 启动承载 InputReader 的线程
result = mReader->start();
if (result) {
ALOGE("Could not start InputReader due to error %d.", result);
mDispatcher->stop();
return result;
}
return OK;
}
InputManager 的启动过程很简单,调用 InputDispatcher 和 InputReader 的 start() 函数,启动承载它们运行的线程,来看一下它们是如何启动线程的。
2.4、InputDispatcher # start()
xref: /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;
}
在方法内,首先判断 mThread 是否已存在,存在则直接返回,不存在则通过 std::make_unique 函数来构建 InputThread 的实例对象,但没有看到启动线程的代码逻辑,带着这个疑问,我们再去看看 InputReader 的 start() 方法。
2.5、InputReader # start()
xref: /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;
}
方法的代码逻辑跟 InputDispatcher 的差不多,也是没有看到启动线程的代码逻辑,既然都是新建 InputThread 对象,那就具体来看一下 InputThread 类。
2.6、InputThread
xref: /frameworks/native/services/inputflinger/include/InputThread.h
class InputThread {
public:
explicit InputThread(std::string name, std::function<void()> loop,
std::function<void()> wake = nullptr);
virtual ~InputThread();
bool isCallingThread();
private:
std::string mName; // 线程名
std::function<void()> mThreadWake;
sp<Thread> mThread; // 承载 InputDispatcher\InputReader 运行的线程
};
xref: /frameworks/native/services/inputflinger/InputThread.cpp
class InputThreadImpl : public Thread {
public: // explicit 关键字的作用就是防止类构造函数的隐式自动转换,且只对有一个参数的类构造函数有效
explicit InputThreadImpl(std::function<void()> loop)
: Thread(/* canCallJava */ true), mThreadLoop(loop) {}
~InputThreadImpl() {}
private:
std::function<void()> mThreadLoop; // 存储一个可调用对象,这里指的是 lambda 表达式
bool threadLoop() override {
mThreadLoop();
return true;
}
};
InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
: mName(name), mThreadWake(wake) {
// 使用封装的可调用对象 loop 新建 InputThreadImpl 对象
mThread = new InputThreadImpl(loop);
// 启动 InputThreadImpl 线程
mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
}
std::function:是一个通用的函数封装类,它可以存储、复制和调用任意可调用对象,包括函数指针、函数对象、成员函数指针和lambda 表达式等。通过使用 std::function 作为函数参数,我们可以实现更加灵活的函数调用方式,提高代码的可读性和可维护性。
由代码可知 InputThread 类其本身不是一个线程,其内部是 InputThreadImpl 类来实现线程的具体功能。使用封装的可调用对象 loop 构建 InputThreadImpl 对象,然后调用其 run() 函数来启动线程。
InputThreadImpl 类继承自 Thread 类,而 C++ 的 Thread 类提供了一个名为 threadLoop() 的纯虚函数,当线程开始运行后,将会在内建的线程循环中不断地调用 threadLoop() 函数,直到此函数返回 false,则退出线程循环结束线程。但从 InputThreadImpl 类的定义可以看出,threadLoop() 函数会一直保持循环(返回值始终为 true),并且每一次循环,会调用一次 mThreadLoop() 函数,而 mThreadLoop() 函数是由 InputDispacher 和 InputReader 在启动时封装好传入的可调用对象。
到这里,终于搞明白了,在 InputDispatcher 启动时,会创建一个线程,然后循环调用 dispatchOnce() 函数,同样 InputReader 启动时,也会创建一个线程,然后循环调用 loopOnce() 函数。
3、IMS 系统就绪
上面两节已完成 IMS 及其重要参与者的构建,并启动了 IMS 系统,接下来就是通知系统 IMS 系统已完成启动并准备就绪,具体看一下源码
xref: /frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
......
// IMS 系统内部的 Handler,用来处理键盘等输入设备有关的消息
private final InputManagerHandler mHandler;
private WiredAccessoryCallbacks mWiredAccessoryCallbacks; // 有线连接的设备回调
private boolean mSystemReady; // 标志系统是否准备完毕
private NotificationManager mNotificationManager; // 通知管理
......
public void systemRunning() {
......
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
synchronized (mLidSwitchLock) {
mSystemReady = true;
......
int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
}
}
// 监听广播,通知 native 层加载键盘布局
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 其内部继续调用 reloadKeyboardLayouts() 函数
updateKeyboardLayouts();
}
}, filter, null, mHandler);
// 监听广播,通知 native 层加载设备别名
filter = new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
reloadDeviceAliases();
}
}, filter, null, mHandler);
// 通过 InputManagerHandler 发送消息来通知 native 层加载键盘布局和加载设备别名
mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
// 如果与系统连接的有线设备的回调不为空,则须回调通知其输入系统 IMS 已准备完毕
if (mWiredAccessoryCallbacks != null) {
mWiredAccessoryCallbacks.systemReady();
}
}
private void reloadKeyboardLayouts() {
if (DEBUG) {
Slog.d(TAG, "Reloading keyboard layouts.");
}
// 调用 native 层函数来加载键盘布局
nativeReloadKeyboardLayouts(mPtr);
}
private void reloadDeviceAliases() {
if (DEBUG) {
Slog.d(TAG, "Reloading device names.");
}
// 调用 native 层函数来加载设备别名
nativeReloadDeviceAliases(mPtr);
}
......
}
注册监听广播,通知 native 层加载键盘布局、设备别名,最后通过 JNI 调用 native 层的函数来加载键盘布局、设备别名。此外,如果与系统连接的有线设备注册的回调不为空,则需回调通知其输入系统 IMS 已准备就绪。
三、总结
1、IMS 启动时序图
2、IMS 成员关系图
最后结合 IMS 的启动时序图和成员关系图可以更深刻的理解 IMS 系统的构成与启动过程,下一篇文章来继续探索输入系统的重要组成成员,等待后续吧!
参考
- 深入理解Android:卷III