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

深入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 设置 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 的启动流程可以分为以下三个步骤:

  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.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());
    }
}

方法中,获取 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 层的 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);
}
  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"事件,获取事件,然后把事件加工后,发送给 InputClassfier
	mReader = 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 lock
        std::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.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 的启动过程如下:

  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() 函数来启动线程。

InputThreadImpl 类继承自 Thread 类,而 C++Thread 类提供了一个名为 threadLoop() 的纯虚函数,当线程开始运行后,将会在内建的线程循环中不断地调用 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.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 启动时序图

IMS 启动时序图

2、IMS 成员关系图

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

参考

  1. 深入理解Android:卷III

http://www.kler.cn/a/148892.html

相关文章:

  • 探索MoviePy:Python视频编辑的瑞士军刀
  • 【CSS】什么是BFC?
  • 鸿蒙之多选框(Checkbox)
  • UAC2.0 speaker——同时支持 16bit,24bit 和 32bit
  • netmap.js:基于浏览器的网络发现工具
  • python解析网页上的json数据落地到EXCEL
  • linux下ffmpeg安装
  • 2023极客大挑战-AGRT战队wp
  • 多线程(补充知识)
  • [nlp] tokenizer
  • 与中通支付对接
  • 前端 vue 面试题(二)
  • leaflet对线设置渐变色
  • LLM大语言模型
  • 深入redis过程-命令
  • 代码随想录算法训练营第四十九天【动态规划part10】 | 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II
  • Android:从源码看FragmentManager如何工作
  • Python内置类属性`__name__`属性的使用教程
  • WPF中DataGrid解析
  • Webshell混淆免杀的一些思路
  • 成绩排序(练习链表)
  • 《数据结构、算法与应用C++语言描述》-二叉树与其他树-二叉树的C++实现-设置信号放大器与并查集问题
  • Positive Technologies 公司发布了一种保护容器环境的产品 PT Container Security
  • Android 13 - Media框架(14)- OpenMax(四)
  • 开源C++智能语音识别库whisper.cpp开发使用入门
  • Pytest自动化测试框架完美结合Allure