Android触摸事件的传递(二)--输入系统InputManagerService

了解更多,移步Android触摸事件传递机制系列详解

1 概述

  1. 当用户触摸屏幕或者按键操作,首次触发的是硬件驱动,驱动收到事件后,将该相应事件写入到输入设备节点, 这便产生了最原生态的内核事件。
  2. 接着,输入系统取出原生态的事件,经过层层封装后成为KeyEvent或者MotionEvent
  3. 最后,交付给相应的目标窗口(Window)来消费该输入事件。可见,输入系统在整个过程起到承上启下的衔接作用。

2 Input模块的主要组成:

  1. Native层的InputReader负责从EventHub取出事件并处理,再交给InputDispatcher
  2. Native层的InputDispatcher接收来自InputReader的输入事件,并记录WMS的窗口信息,用于派发事件到合适的窗口
  3. Java层的InputManagerService跟WMS交互,WMS记录所有窗口信息,并同步更新到IMS,为InputDispatcher正确派发事件到ViewRootImpl提供保障;

3 整体框架类图

InputManagerService作为system_server中的重要服务,继承于IInputManager.Stub, 作为Binder服务端,那么Client位于InputManager的内部通过IInputManager.Stub.asInterface()获取Binder代理端,C/S两端通信的协议是由IInputManager.aidl来定义的。

input_binder.jpg

图解:

  • InputManagerService位于Java层的InputManagerService.java文件;
    a. 其成员mPtr指向Native层的NativeInputManager对象;
  • NativeInputManager位于Native层的com_android_server_input_InputManagerService.cpp文件;
    a. 其成员mServiceObj指向Java层的IMS对象;
    b. 其成员mLooper是指“android.display”线程的Looper;
  • InputManager位于libinputflinger中的InputManager.cpp文件;
    a. InputDispatcherInputReader的成员变量mPolicy都是指NativeInputManager对象;
    b. InputReader的成员mQueuedListener,数据类型为QueuedInputListener;通过其内部成员变量mInnerListener指向InputDispatcher对象; 这便是InputReaderInputDispatcher交互的中间枢纽。

4 启动调用栈(流程)

IMS服务是伴随着system_server进程的启动而启动,整个调用过程:

InputManagerService(初始化)
    nativeInit
        NativeInputManager
            EventHub
            InputManager
                InputDispatcher
                    Looper
                InputReader
                    QueuedInputListener
                InputReaderThread
                InputDispatcherThread
IMS.start(启动)
    nativeStart
        InputManager.start
            InputReaderThread->run
            InputDispatcherThread->run

整个过程首先创建如下对象:NativeInputManagerEventHubInputManagerInputDispatcherInputReaderInputReaderThreadInputDispatcherThread。 接着便是启动两个工作线程InputReader,InputDispatcher

5 IMS启动过程

private void startOtherServices() {
    //1. 初始化IMS对象
    inputManager = new InputManagerService(context);
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
    ...
    //将InputMonitor对象保持到IMS对象
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    //2
    inputManager.start();
}

通过上述代码接下,分InputManagerService初始化和InputManagerService的启动来写

6 InputManagerService初始化

6.1 构造方法

创建InputManagerService对象--构造方法:[-> InputManagerService.java]

public InputManagerService(Context context) {
     this.mContext = context;
     // 运行在线程"android.display"
     this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
     ...

     //初始化native对象
     mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
     LocalServices.addService(InputManagerInternal.class, new LocalService());
 }

6.2 构造方法中调用nativeInit

[-> com_android_server_input_InputManagerService.cpp]

static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    //获取native消息队列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ...
    //创建Native的InputManager【见小节2.3】
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im); //返回Native对象的指针
}

6.3 nativeInit中创建NativeInputManager

[-> com_android_server_input_InputManagerService.cpp]

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();
    mContextObj = env->NewGlobalRef(contextObj); //上层IMS的context
    mServiceObj = env->NewGlobalRef(serviceObj); //上层IMS对象
    ...
    sp<EventHub> eventHub = new EventHub(); // 创建EventHub对象【见小节2.4】
    mInputManager = new InputManager(eventHub, this, this); // 创建InputManager对象
}

6.4 NativeInputManager中创建EventHub

[-> EventHub.cpp]

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //创建epoll
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

    mINotifyFd = inotify_init();
    //此处DEVICE_PATH为"/dev/input",监听该设备路径
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    //添加INotify到epoll实例
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    int wakeFds[2];
    result = pipe(wakeFds); //创建管道

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    //将pipe的读和写都设置为非阻塞方式
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);

    eventItem.data.u32 = EPOLL_ID_WAKE;
    //添加管道的读端到epoll实例
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ...
}

该方法主要功能:

  • 初始化INotify(监听”/dev/input”),并添加到epoll实例
  • 创建非阻塞模式的管道,并添加到epoll;

6.5 NativeInputManager中创建InputManager

[-> InputManager.cpp]

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //创建InputDispatcher对象
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    //创建InputReader对象
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputDispatcherInputReadermPolicy成员变量都是指NativeInputManager对象。

6.6 InputManager中创建InputDispatcher

[-> InputDispatcher.cpp]

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
    mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    //创建Looper对象
    mLooper = new Looper(false);

    mKeyRepeatState.lastKeyEntry = NULL;
    //获取分发超时参数
    policy->getDispatcherConfiguration(&mConfig);
}

该方法主要工作:

  • 创建属于自己线程的Looper对象;
  • 超时参数来自于IMS,参数默认值keyRepeatTimeout= 500,keyRepeatDelay = 50。

6.7 InputManager中创建InputReader

[-> InputReader.cpp]

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 创建输入监听对象
    mQueuedListener = new QueuedInputListener(listener);
    {
        AutoMutex _l(mLock);
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    }
}

此处mQueuedListener的成员变量mInnerListener便是InputDispatcher对象。 InputManager创建完InputDispatcherInputReader对象, 接下里便是调用initialize初始化。

6.8 InputManager中初始化initialize

InputManager创建完InputDispatcher和InputReader对象, 接下里便是调用initialize初始化。
[-> InputManager.cpp]

void InputManager::initialize() {
    //创建线程“InputReader”
    mReaderThread = new InputReaderThread(mReader);
    //创建线程”InputDispatcher“
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
        Thread(/*canCallJava*/ true), mReader(reader) {
}

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
        Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

初始化的主要工作就是创建两个能访问Java代码的native线程。

  • 创建线程“InputReader”
  • 创建线程”InputDispatcher“

整个的InputManagerService对象初始化过程并完成,接下来便是调用其start方法。

7 IMS.start

[-> InputManagerService.java]

public void start() {
    // 启动native对象[见小节2.10]
    nativeStart(mPtr);

    Watchdog.getInstance().addMonitor(this);

    //注册触摸点速度和是否显示功能的观察者
    registerPointerSpeedSettingObserver();
    registerShowTouchesSettingObserver();

    mContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updatePointerSpeedFromSettings();
            updateShowTouchesFromSettings();
        }
    }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);

    updatePointerSpeedFromSettings(); //更新触摸点的速度
    updateShowTouchesFromSettings(); //是否在屏幕上显示触摸点
}

7.1 start中调用nativeStart

[-> com_android_server_input_InputManagerService.cpp]

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    //此处ptr记录的便是NativeInputManager
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // [见小节7.2]
    status_t result = im->getInputManager()->start();
    ...
}

7.2 InputManager.start

[InputManager.cpp]

status_t InputManager::start() {
    result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    ...
    return OK;
}

该方法的主要功能是启动两个线程:

  • 启动线程“InputReader”
  • 启动线程”InputDispatcher“

8 总结

分层视角:

  1. Java层InputManagerService:采用android.display线程处理Message.
  2. JNI的NativeInputManager:采用android.display线程处理Message,以及创建EventHub
  3. Native的InputManager:创建InputReaderThreadInputDispatcherThread两个线程

主要功能:

  • IMS服务中的成员变量mPtr记录Native层的NativeInputManager对象;
  • IMS对象的初始化过程的重点在于native初始化,分别创建了以下对象:
    NativeInputManager
    EventHub, InputManager
    InputReaderInputDispatcher
    InputReaderThreadInputDispatcherThread
  • IMS启动过程的主要功能是启动以下两个线程:
    InputReader:从EventHub取出事件并处理,再交给InputDispatcher
    InputDispatcher:接收来自InputReader的输入事件,并派发事件到合适的窗口。

从整个启动过程,可知有system_server进程中有3个线程跟Input输入系统息息相关,分别是android.display, InputReader,InputDispatcher

  • InputDispatcher线程:属于Looper线程,会创建属于自己的Looper,循环分发消息;
  • InputReader线程:通过getEvents()调用EventHub读取输入事件,循环读取消息;
  • android.display线程:属于Looper线程,用于处理Java层的IMS.InputManagerHandler和JNI层的NativeInputManager中指定的MessageHandler消息;
事件分发1.png

参考

Android系统源码分析-事件收集
Android 输入系统(一)InputManagerService
Input系统—启动篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,471评论 25 707
  • 沉闷的空气,像阴暗的囚笼 静谧的氛围,像审讯的牢房 人们都低沉着原本高傲的头颅,向着罪恶的王冠屈服。 原本跳动的心...
    流年已尽阅读 521评论 3 2
  • 我们都是赤条条的来到这个世,是从什么时候开始,人与人之间有了那么多的天差地别?~ 不得不说,当我们第一声啼哭后,被...
    minnameng阅读 216评论 0 2
  • 今年已经88岁的公公,依然坚持每天写字一小时,开始再拿起毛笔写字,是在公公退休几年后的事儿,从此断断续续二十余年,...
    灰玫瑰汪然阅读 144评论 0 3