做网站类的书本信息,辽宁工程造价信息网官网,做购物网站的目的,现代网站建设公司文章目录 前言一、输入系统的基本组成部分二、输入系统相关源码分析1、IMS 构建1.1、SystemServer # startOtherServices()1.2、InputManagerService1.3、NativeInputManager # nativeInit()1.4、NativeInputManager1.5、InputManager1.6、InputDispatcher1.7、InputReader1.8、… 文章目录 前言一、输入系统的基本组成部分二、输入系统相关源码分析1、IMS 构建1.1、SystemServer # startOtherServices()1.2、InputManagerService1.3、NativeInputManager # nativeInit()1.4、NativeInputManager1.5、InputManager1.6、InputDispatcher1.7、InputReader1.8、EventHub1.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 可以从中读取事件InputManagerServiceAndroid 系统服务以后简称 IMS其分为 Java 层和 Native 层两部分。Java 层负责与 WindowManagerService 通信。而 Native 层则是InputReader 和 InputDispatcher 两个输入系统关键组件的运行容器EventHub直接访问所有的设备节点。并且正如其名字所描述的它通过一个名为 getEvents( ) 的函数将所有输入系统相关的待处理的底层事件返回给使用者包括原始输入事件、设备节点的增删等。InputReaderIMS 中的关键组件之一运行于一个独立的线程中负责管理输入设备的列表与配置以及进行输入事件的加工处理。通过其线程循环不断地通过 getEvent( ) 函数从 EventHub 中将事件取出并进行处理。对于设备节点的增删事件将会更新输入设备列表与配置。对于原始输入事件InputReader 对其进行翻译、组装、封装为包含更多信息、更具可读性的输入事件然后交给 InputDispatcher 进行派发InputReaderPolicy为 InputReader 的事件加工处理提供一些策略配置例如键盘布局信息等InputDispatcherIMS 中的另一个关键组件也运行于一个独立的线程中。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 设置 WindowManagerServicemActivityManagerService.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.Stubimplements 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 层的 MessageQueuespMessageQueue 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 层的 IMSIMS 将其保存在 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 spLooper looper);inline spInputManagerInterface getInputManager() const { return mInputManager; }private:spInputManagerInterface mInputManager; jobject mServiceObj; // IMS 对象spLooper mLooper; // Looper 对象Mutex mLock;struct Locked {......例如mLocked.showTouches 是// 如果为 True则启用指针手势bool pointerGesturesEnabled;// 由开发者选项中 Show taps 决定的其功能是在屏幕上显示一个触摸点bool showTouches;......} mLocked GUARDED_BY(mLock);std::atomicbool mInteractive;......static inline JNIEnv* jniEnv() {return AndroidRuntime::getJNIEnv();}
};NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const spLooperlooper) :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 spInputReaderPolicyInterface readerPolicy,const spInputDispatcherPolicyInterface dispatcherPolicy);......
private:spInputReaderInterface mReader;spInputClassifierInterface mClassifier;spInputDispatcherInterface mDispatcher;
};xref: /frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(const spInputReaderPolicyInterface readerPolicy,const spInputDispatcherPolicyInterface dispatcherPolicy) {// 创建 InputDispatcher 对象使用 InputDispatcherPolicyInterface 接口用于对事件进行分发mDispatcher createInputDispatcher(dispatcherPolicy);// 创建 InputClassifier 对象使用 InputListenerInterface用于对事件分类mClassifier new InputClassifier(mDispatcher);// 创建 InputReader 对象使用 InputReaderPolicyInterface 和 InputListenerInterface// 其通过 EventHub 监听/dev/input事件获取事件然后把事件加工后发送给 InputClassfiermReader 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
spInputDispatcherInterface createInputDispatcher(const spInputDispatcherPolicyInterface 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 spInputDispatcherPolicyInterface policy);
......
private:std::unique_ptrInputThread mThread;spInputDispatcherPolicyInterface mPolicy;spLooper mLooper;spInputReporterInterface mReporter;
};xref: /frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
InputDispatcher::InputDispatcher(const spInputDispatcherPolicyInterface 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
spInputReaderInterface createInputReader(const spInputReaderPolicyInterface policy,const spInputListenerInterface listener) {// 创建 EventHub 对象传入到 InputReader 的构造函数中来新建 InputReader 对象return new InputReader(std::make_uniqueEventHub(), policy, listener);
}该方法里面在新建 InputReader 对象时结合 InputReader 类的构造函数可知第一个参数是 EventHub 的实例对象那么 EventHub 对象是怎么创建的呢
这里需要知道一些 C 有关的知识std::make_unique 的语法如下
templatetypename T, typename... Args
std::unique_ptrT make_unique(Args... args);std::make_unique是 C11 标准引入的一个模版函数用于动态分配指定类型的内存并返回一个指向分配内存的唯一指针 (即 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_ptrEventHubInterface eventHub,const spInputReaderPolicyInterface policy,const spInputListenerInterface listener);virtual ~InputReader();
protected:// 在循环过程的每次迭代中InputReader 读取并处理一条来自 EventHub 的传入消息void loopOnce();private:std::unique_ptrInputThread mThread;std::shared_ptrEventHubInterface mEventHub;spInputReaderPolicyInterface mPolicy;spQueuedInputListener mQueuedListener;
};xref: /frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptrEventHubInterface eventHub,const spInputReaderPolicyInterface policy,const spInputListenerInterface 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 lockstd::scoped_lock _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}与构建 InputDispatcher 对象类似在调用 InputReader 的构造函数构建实例对象的同时将入参 policy 赋值给 mPolicyeventHub 对象赋值给 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_mapint32_t, std::unique_ptrDevice mDevices;std::vectorstd::unique_ptrDevice mOpeningDevices;std::vectorstd::unique_ptrDevice 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(),mOpeningDevices(nullptr),mClosingDevices(nullptr),mNeedToSendFinishedDeviceScan(false),mNeedToReopenDevices(false),mNeedToScanDevices(true),mPendingEventCount(0),mPendingEventIndex(0),mPendingINotify(false) {ensureProcessCanBlockSuspend();// 创建 Epoll 对象的描述符监听设备节点是否有数据可读(有无事件发生)mEpollFd epoll_create1(EPOLL_CLOEXEC);LOG_ALWAYS_FATAL_IF(mEpollFd 0, Could not create epoll instance: %s, strerror(errno));// 创建 INotify 对象用于监听设备节点的路径 /dev/input是否有变化如有设备增删则对应的设备节点的文件也会增删mINotifyFd inotify_init();// 添加 watch 监听存储设备节点的路径 DEVICE_PATH 的创建与删除当有设备节点发生变化时通过 INotify 对象可以读取事件的详细信息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);}// 构建 epoll_event 结构体并为每一个需要监控的描述符填充该结构体以描述监控事件struct epoll_event eventItem {};eventItem.events EPOLLIN | EPOLLWAKEUP; // 事件掩码指明需要监听的事件类型可读eventItem.data.fd mINotifyFd; // 数据字段设置需要监听的描述符这里是 mINotifyFd即监听设备节点的路径// 调用 epoll_ctl() 函数将 INotify 对象注册到 Epoll 中监听其文件描述符对应的文件夹下是否有设备节点的增删信息// 第一个参数即前面创建的 Epoll 对象的描述符第二个参数表示具体操作这里 ADD 表示增加注册事件// 第三个参数表示需要监听的描述符第四个参数是描述监听事件的详细信息的 epoll_event 结构体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);// 创建匿名管道 wakeFds并将读端交给 Epoll写端交给 InputReader用于唤醒 Epoll避免其阻塞在 epoll_wait()int wakeFds[ 2];result pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result ! 0, Could not create wake pipe. errno%d, errno);// mWakeReaderFD 和 mWakeWriterFD 对应管道的两端mWakeReadPipeFd wakeFds[0];mWakeWritePipeFd wakeFds[1];result fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result ! 0, Could not make wake read pipe non-blocking. errno%d, errno);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_ctl() 函数可重复调用将多个文件描述符的多种事件监听注册到 Epoll 对象中// 将匿名管道的读取端的描述符也注册到 Epoll 中用于监听读取端的可读事件当写入端有数据写入时// 管道的读取端就有数据可读使得 epoll_wait() 得以返回从而达到唤醒 InputReader 线程的目的避免其一直阻塞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);
}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.Stubimplements Watchdog.Monitor {......private static native void nativeStart(long ptr);......public void start() {Slog.i(TAG, Starting input manager);// 启动 native 层的 IMSnativeStart(mPtr);Watchdog.getInstance().addMonitor(this);// 监听Settings.System.POINTER_SPEED这个表示手指的速度registerPointerSpeedSettingObserver();// 监听Settings.System.SHOW_TOUCHES这个表示是否在屏幕上显示触摸坐标registerShowTouchesSettingObserver();registerAccessibilityLargePointerSettingObserver();registerLongPressTimeoutObserver();registerMaximumObscuringOpacityForTouchSettingObserver();registerBlockUntrustedTouchesModeSettingObserver();// 监听用户切换mContext.registerReceiver(new BroadcastReceiver() {Overridepublic 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_castNativeInputManager*(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_uniqueInputThread(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_uniqueInputThread(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::functionvoid() loop,std::functionvoid() wake nullptr);virtual ~InputThread();bool isCallingThread();
private:std::string mName; // 线程名std::functionvoid() mThreadWake;spThread mThread; // 承载 InputDispatcher\InputReader 运行的线程
};xref: /frameworks/native/services/inputflinger/InputThread.cpp
class InputThreadImpl : public Thread {
public: // explicit 关键字的作用就是防止类构造函数的隐式自动转换且只对有一个参数的类构造函数有效explicit InputThreadImpl(std::functionvoid() loop): Thread(/* canCallJava */ true), mThreadLoop(loop) {}~InputThreadImpl() {}private:std::functionvoid() mThreadLoop; // 存储一个可调用对象这里指的是 lambda 表达式bool threadLoop() override {mThreadLoop();return true;}
};InputThread::InputThread(std::string name, std::functionvoid() loop, std::functionvoid() 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.Stubimplements 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() {Overridepublic void onReceive(Context context, Intent intent) {// 其内部继续调用 reloadKeyboardLayouts() 函数updateKeyboardLayouts();}}, filter, null, mHandler);// 监听广播通知 native 层加载设备别名filter new IntentFilter(BluetoothDevice.ACTION_ALIAS_CHANGED);mContext.registerReceiver(new BroadcastReceiver() {Overridepublic 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