深入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 输入事件,然后在所有的窗口中寻找合适的事件接收者,并派发给它来消费该输入事件。可见,输入系统在整个输入事件处理过程中起到了承上启下的衔接作用。


一、输入系统的基本组成部分

IMS 的总体流程与参与者
上图展示了输入事件的处理流程以及输入系统中最基本的参与者,下面简要介绍一下各个参与者:

  • Linux 内核:接受输入设备的中断,并将原始输入事件的数据写入设备节点中;
  • 设备节点:内核与 InputManagerService 的桥梁,它将原始事件的数据暴露给用户空间,以便 InputManagerService 可以从中读取事件;
  • InputManagerService:Android 系统服务,以后简称 IMS,其分为 Java 层和 Native 层两部分。Java 层负责与 WindowManagerService 通信。而 Native 层则是InputReaderInputDispatcher 两个输入系统关键组件的运行容器;
  • EventHub:直接访问所有的设备节点。并且正如其名字所描述的,它通过一个名为 getEvents( ) 的函数将所有输入系统相关的待处理的底层事件返回给使用者,包括原始输入事件、设备节点的增删等。
  • InputReaderIMS 中的关键组件之一,运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。通过其线程循环不断地通过 getEvent( ) 函数从 EventHub 中将事件取出并进行处理。对于设备节点的增删事件,将会更新输入设备列表与配置。对于原始输入事件,InputReader 对其进行翻译、组装、封装为包含更多信息、更具可读性的输入事件,然后交给 InputDispatcher 进行派发;
  • InputReaderPolicy:为 InputReader 的事件加工处理提供一些策略配置,例如键盘布局信息等;
  • InputDispatcherIMS 中的另一个关键组件,也运行于一个独立的线程中。InputDispatcher 中保管了来自 WindowManagerService 的所有窗口的信息,其收到来自 InputReader 的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口;
  • InputDispatcherPolicy:为 InputDispatcher 的派发过程提供策略控制。例如截取某些特定的输入事件用作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是 HOME 键InputDispatcherPolicy 截取到 PhoneWindowManager 中进行处理,并阻止窗口收到 HOME 键按下的事件;
  • WindowManagerService:虽不是输入系统中的成员,但却对 InputDispatcher 的正常工作起到了至关重要的作用。当新建窗口时,WMS 为新窗口和 IMS 之间创建了事件传递所用的通道。另外,WMS 还将所有窗口的信息,包括窗口的可点击区域、焦点窗口等信息,实时地更新到 IMSInputDispatcher 中,使得 InputDispatcher 可以正确地将事件派发到指定的窗口;
  • ViewRootImpl:对某些窗口,如壁纸窗口、SurfaceView 的窗口来说,窗口就是输入事件派发的终点。而对其他的如 Activity、对话框等使用了 Android 控件系统的窗口来说,输入事件的终点是控件 ViewViewRootImpl 将窗口所接收的输入事件沿着控件树将事件派发给感兴趣的控件 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 的启动流程可以分为以下三个步骤:

  1. 构建 IMS 实例对象,并建立上层与底层的映射关系。
  2. 启动 IMS,其内部就是启动 native 层输入系统的几个重要参与者(后续会分析)。
  3. 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());}
}

方法中,获取 DisplayThreadLooper,新建 InputManagerHandler 对象,然后调用 native 层的 nativeInit() 函数,创建NativeInputManager 对象,最后新建 IMS 的本地系统服务 LocalService,其继承自 InputManagerInternal 抽象接口,并加入到 LocalServices 中。

DisplayThreadsystem_server 进程中是单例的,且只能被 WindowManagerDisplayManagerInputManager 使用。

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 层的 MessageQueuesp<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);
}
  1. NativeInputManager 的构造函数中,创建一个全局引用,并通过 mServiceObj 指向 Java 层的 IMS 对象,便于后续可以通过 mServiceObj 调用 JavaIMS 对象的方法。
  2. 初始化参数,这里要注意一个结构体变量 mLocked,它的一些参数都是由 Java 层控制的。
  3. 然后将自己作为参数来新建 InputManager 对象,并将 InputManager 注册到 ServiceManager 中,InputManager 才是 native 层输入系统的服务。

注意:由 NativeInputManager 类的声明可以看到,其实现了 InputReaderPolicyInterfaceInputDispatcherPolicyInterface 两个接口。

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"事件,获取事件,然后把事件加工后,发送给 InputClassfiermReader = createInputReader(readerPolicy, mClassifier);
}

InputManager 内部创建了三个子模块:InputReaderInputClassifierInputDispatcher,其作用如下:

  1. InputReader:负责从 EventHub 中获取事件,然后把事件加工后,发送给 InputClassfier
  2. InputClassifer:负责把事件发送给 InputDispatcher,但是它会对触摸事件进行一个分类工作。
  3. InputDispatcher:对进行事件分发。

此外,在上一小节的分析中,我们知道在构建 InputManager 实例对象时使用了两个 this 参数,而 InputManager 构造函数需要的两个接口参数正是由 NativeInputManager 实现的,而具体使用这两个接口的不是 InputManager 自身,而是它内部的子模块 InputDispatcherInputReader

InputDispatcherInputReader 在构建时都传递了 NativeInputManager 对象参数,并赋值到各自的 mPolicy 变量,后续可直接通过 mPolicy 调用 JavaIMS 对象方法,因此 InputManagerJava 层通信的能力是由子模块 InputDispatcherInputReader 实现的。

接下来首先来看看 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 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_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 的构造函数主要工作有:

  1. 新建并初始化 EpollINotify 对象等。
  2. 调用 inotify_add_watch 函数,监听 “/dev/input” 目录下的设备节点创建与删除操作,然后通过 read() 函数读取事件。
  3. INotify 添加到 Epoll 中,作为一个监控对象。
  4. 创建管道,将管道读取端的可读事件添加到 Epoll 中,使 epoll_wait() 函数返回,唤醒 InputReader 线程。

至此,IMSJava 层和 native 层的实例对象都已创建完成,并且在这个过程中,输入系统的重要参与者也均创建完成。

1.9、小结

IMS的结构体系
Java 层的 IMS 的主要工作是为 ReaderPolicyDispatcherPolicy 提供实现,以及与 Android 其他系统服务进行协作,其中最主要的协作者是 WMS

NativeInputManager 位于 IMSJNI 层,负责 Native 层的组件与 Java 层的 IMS 之间的相互通信。同时为 InputReaderInputDispatcher 提供了策略请求的接口。策略请求被他转发给 Java 层的 IMS,由 IMS 进行最终的决策定夺。

InputManagerInputReaderInputDispatcher 的运行容器,在启动 InputReaderInputDispatcher 时,分别新建自己的运行线程 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 的启动过程如下:

  1. 启动 native 层输入系统,其实就是启动刚刚说到的 InputReaderInputDispatcher
  2. 注册监听广播,因为这些广播与输入系统的配置有关,当接收到这些广播,会更新配置到 native 层。
  3. 直接读取配置,更新到 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 的启动过程很简单,调用 InputDispatcherInputReader 的 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() 函数来启动线程。

当线程启动后,会循环调用 threadLoop() 函数,直到这个函数返回 false。但从 InputThreadImpl 类的定义可以看出,threadLoop() 函数会一直保持循环(返回值始终为 true),并且每一次循环,会调用一次 mThreadLoop() 函数,而 mThreadLoop() 函数是由 InputDispacherInputReader 在启动时封装好传入的可调用对象。

到这里,终于搞明白了,在 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 启动时序图

IMS 启动时序图

2、IMS 成员关系图

IMS 成员关系图
最后结合 IMS 的启动时序图和成员关系图可以更深刻的理解 IMS 系统的构成与启动过程,下一篇文章来继续探索输入系统的重要组成成员,等待后续吧!

参考

  1. 深入理解Android:卷III

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/225847.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战Flask+BootstrapTable最实用服务端分页查询动态表头及数据(ajax方式)

看到这篇文章的朋友们是幸运的,我用了很久才实战出如下结果,且行且珍惜,祝好! 话不多说,有图有源码 1.看图,实现服务端动态表头数据,分页,查询,排序 1.数据准备 CREATE TABLE goods (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL COMMENT 商品名,no …

mac电脑下载Netflix Mac(奈飞客户端)安装教程

Netflix Mac&#xff0c;奈飞官方客户端&#xff0c;带给您无限的电影和剧集体验&#xff01;与朋友分享最新热门剧集、电影&#xff0c;与家人一起享受高品质的流媒体内容。 通过Netflix Mac&#xff0c;您可以轻松地搜索、浏览和观看各种类型的影片&#xff0c;包括剧情片、…

Photoshop Elements 2023 v21.0(ps简化版)

Photoshop Elements 2023是一款ps简化版图像处理软件&#xff0c;它加入了一些新的功能和工具&#xff0c;以帮助用户更高效地处理图片。 新功能&#xff1a;软件加入了黑科技&#xff0c;采用Adobe Sensei AI技术&#xff0c;主打人工智能&#xff0c;一键P图&#xff0c;新增…

使用Python进行帧提取及指定帧插入实现文本隐写(CTF杂项出题方向)

视频是由一系列连续的图像帧组成的&#xff0c;每一帧都是视频的静止画面。这些帧以一定的速率播放&#xff0c;就形成了运动的视频。以下是视频帧的一些基本概念&#xff1a; 帧率&#xff08;Frame Rate&#xff09;&#xff1a; 帧率表示每秒播放的图像帧数量。通常以“帧/秒…

c语言,输入整数n(行数,本例为4),按照如下规则打印数字图片 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16

c语言&#xff0c;输入整数n(行数&#xff0c;本例为4&#xff09;&#xff0c;按照如下规则打印数字图片 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 以下是使用C语言编写的程序&#xff0c;根据输入的行数打印数字图片的规则&#xff1a; #include <stdio.h>int main() …

[SaaS] 广告创意中stable-diffusion的应用

深度对谈&#xff1a;广告创意领域中 AIGC 的应用这个领域非常快速发展&#xff0c;所以你应该保持好奇心&#xff0c;不断尝试新事物&#xff0c;不断挑战自己。https://mp.weixin.qq.com/s/ux9iEABNois3y4wwyaDzAQ我对AIGC领域应用调研&#xff0c;除了MaaS服务之外&#xff…

事务的自动提交机制和隐式提交机制

自动提交机制就是一个sql语句完成默认提交一次&#xff0c;也就是说一个sql语句是原子性的。想关闭这种功能&#xff0c;两种方式一种写START TRANSACTION&#xff0c;另一种SET autocommit OFF 隐式提交机制&#xff0c;在START TRANSACTION后&#xff0c;会有一些情况导致语…

【刷题笔记】分糖果||数组||暴力通过||符合思维方式||多案例分析

分发糖果 文章目录 分发糖果1 题目描述2 题目分析2.1 寻找波峰波谷2.2 从波底往波峰攀爬&#xff01;2.2 计算糖果 3 代码附录1 1 题目描述 https://leetcode.cn/problems/candy/ n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&…

数据导入与预处理-第7章-数据清理工具OpenRefine

文章目录 数据清理工具OpenRefineOpenRefine简介下载与安装配置创建项目操作列收起列移动列和重排列移除该列与移除列重新定义列标题撤销与重做导出数据 进阶操作数据排序数据归类重复检测数据填充文本过滤数据转换 总结 数据清理工具OpenRefine OpenRefine简介 OpenRefine是…

NX二次开发UF_MTX3_x_vec 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_MTX3_x_vec Defined in: uf_mtx.h void UF_MTX3_x_vec(const double mtx [ 9 ] , double x_vec [ 3 ] ) overview 概述 Returns the X-direction vector of a matrix. 返回矩阵…

图片照片编辑SDK解决方案

图像和照片已经成为我们日常生活中不可或缺的一部分&#xff0c;无论是个人还是企业&#xff0c;都希望通过高质量的图像和照片来提升品牌形象&#xff0c;吸引更多的用户和客户。然而&#xff0c;图像和照片的编辑并不是一件简单的事情&#xff0c;它需要专业的技术和工具。这…

如何使用ArcGIS实现生态廊道模拟

生态廊道是指一种连接不同生态系统的走廊或通道&#xff0c;其建立有助于解决人类活动对野生动植物栖息地破碎化和隔离化的问题&#xff0c;提高生物多样性&#xff0c;减轻生态系统的压力。在城市化和农业开发不断扩张的背景下&#xff0c;生态廊道对于野生动植物的生存和繁衍…