成鹏致远 | lcw.cnblogs.com | 2013-10-25

Linux输入子系统回顾

1:为什么要回顾linux输入子系统?这个问题后面自然就知道了

  1.linux输入子系统设备是基于平台设备机制的,所以先回顾平台设备机制,主要回顾后面用得到的东西

1.申请主设备号
2.创建cdev->将cdev挂载到系统设备哈希链表中,同时生成inode节点
3.创建device->将device与刚生成的inode节点关联起来,为上层调用提供接口

  2.注册输入子系统设备

1.创建一个设备类class
2.申请主设备号
3.创建cdev->将cdev挂载到系统设备哈希链表中,同时生成inode节点
4.创建input_device->将input_device与刚生成的inode节点关联起来,为事件驱动层提供接口

1.注册设备支持输入事件类型(type)->【这个后面会用到】
2.注册设备支持输入事件编码
3.生成/dev/input_device

      5.当input_device与input_hander匹配成功

1.生成/dev/input*
2.上层应用通过主设备号打开/dev/input,通过次设备号打开/dev/input_device

  3.输入子系统部分函数

1.read函数->读【后面会用到】
2.write函数->写【后面会用到】
3.event函数->上报事件【后面会用到】


【提前思考的问题】

1.Android事件处理系统是怎么捕捉到输入事件的?
2.Android上层应用是怎么捕捉到输入事件的?
3.Android上层应用捕捉到输入事件是怎么响应的?

2.从启动一个Android程序开始

  1.Activity启动流程:onCreate()->onStart()->onResume()->Activity Running

【Q1】:为什么onStart()后不直接Running,要插入一个onResume(),在onResume()中系统作了什么事情?
<A1>:在onResume()中,系统会为该Activity创建一个ViewRoot!

  【Q2】:这个ViewRoot有什么用?它做了哪些事情?这个后面解答

3.Android事件从输入到输出的整个流程

  1.Activity运行时,用户点击触摸屏操作->{事件产生
    1.点击触摸屏,必然会调用触摸屏驱动->{事件输入

【Q3】:事件传递过程?->{事件传递}
<A3>:以下所有步骤!
 1.Android上层应用调用Framework层的JNI本地方法->{事件消息传递到JNI层

 1.实现JNI层方法,填充本地方法映射表,实现提供Android上层应用的接口
 2.生成so动态链接库文件,adb push到/system/lib目录下

 2.JNI本地方法调用HAL层(硬件抽象层:中间件)方法->{事件消息传递到HAL层

 1.JNI层通过指定ID得到HAL模块实例,然后调用HAL层函数
 2.生成so动态链接库文件,adb push到/system/lib/hw目录下

 3.HAL通过系统调用write进入内核层->{事件消息传递到内核

 1.copy_from_user()取得用户层数据
 2.调用input_device.write->{(1.1.2):事件消息传递到驱动层

------------------------------------------------------------------------------------------
 4.输入设备驱动获取事件,调用event函数->{(1.1.3)}
 5.事件处理层根据事件类型进行上报->{事件消息传递到内核事件处理层
   1.调用input_event(device,type,code,value)->{【type】后面会用到}
 6.到事件处理层时,内核会唤醒read函数->{(1.1.1):事件消息传递到内核层
   1.通过copy_to_user()将内核数据传递到用户空间->{事件消息传递到用户空间
    【Q4】:read函数被谁调用的?
 7.事件消息被Android的事件处理系统捕捉到->{事件消息传递到Android事件处理系统

 1.Android事件处理系统将这个消息发送到Android应用层
 【Q5】:事件消息在Android事件处理系统中是怎么传递的?

 8.Android上层获取事件消息,根据事件类型(3.1.1.5.1:type)响应上层View相应回调函数
 9.Android界面UI更新->{事件响应

4.至此为此,项目中用到的框架知识已经介绍完毕

  1.Android事件处理系统作为一个黑盒子,暂时略过
  2.切入到项目解读:电视棒远程遥控器

1.客户端APK

1.数据采集->{此数据必须与Linux输入子系统兼容,这样才能达到欺骗系统的目的}
2.数据传递

2.服务端APK->{实现二个虚拟设备(虚拟键盘设备与虚拟鼠标设备}

1.接收数据
2.将数据传送到虚拟设备
3.虚拟设备上报事件,欺骗系统输入事件发生

5.剖析Android事件处理系统

【Q5】:事件消息在Android事件处理系统中是怎么传递的?
<A5>:看看Android的事件处理系统
1.紧接着3.1.1.7,事件消息传递到Android事件处理系统
2.在Android开机时,系统服务(System Server)会初始化Android窗口管理服务(WindowManagerService
3.WMS服务会初始化InputManagerInputManager是Framework层的一个C++类,负责管理所有的输入事件的捕获与转发

1.在InputManager构造函数中,会初始化事件处理系统中最重要的三个类(InputReader/InputDispatcher/EventHub
2.然后初始化InputManager(InputManager.initialize()),产生两个线程(InputReaderThread/InputDispatcherThread,分别负责事件捕获与事件转发)
3.InputReaderThread线程工作->{解决问题1:Android事件处理系统是怎么捕捉到输入事件的}

    1.调用InputReader::loopOnce(),在InputReader类中,已经实现looper机制,循环操作
      1.调用EventHub::getEvent()函数
        1.调用Epoll_wait()函数
          1.在Epoll_wait()函数中,会去循环读取所有的/dev/input设备,一旦有事件产生,就会被此函数捕捉到

【Q6】:Android事件处理系统与/dev/input设备是如何关联起来的
A6】:EventHub::Device <---->/dev/input,一一对应关系

 2.当有事件发生时,Android事件处理系统会调用read函数->{系统调用,从内核层拿到事件消息}

【Q4】:read函数被谁调用的?
<A4>:read函数是InputReader调用的

      2.将从内核层读取到的事件消息(input_event)保存到RawEvent中

2.根据事件类型(input_event.type)调用相应的消息转换器(InputMapper
  1.Android系统目前支持5种事件消息(翻滑盖/轨迹球/多点触摸/单点触摸/键盘)
3.InputReader::InputDevice::inputMapper将RawEvent事件转换成对应的Notifyargs
  1.InputMapper将RawEvent事件转换成对应的Notifyargs类
  2.将Notifyargs加入到InputRead::QueueListener::argsQueue队列中
4.调用InputReader::QueueListener::flush()函数,处理队列中的事件消息
5.调用Notifyargs::notify函数,将事件消息转换成对应的EventEntry再转交给InputDispatcher

1.对事件进行预处理
  1.判断是否丢弃此事件消息
    1.丢弃,则直接返回
    2.不丢弃
  2.获取当前Activity对应的Connection对象->(这个后面再讲)
2.将对应的EventEntry加入到InputDispatcher::OutBoundQueue队列中
  【Q7】:在Notifyargs是怎么获取InputDispatcher实例的
  <A7>:在初始化InputRead::QueueListener::argsQueue队列时,将InputDispatcher对象传递过来了

6.唤醒InputDispatcher::pollOnce()函数

4.InputDispatcherThread线程工作
  1.调用InputDispatcher::loopOnce(),在InputDispatcher类中,同样实现looper机制,循环操作
    1.调用pollOnce()函数,轮询InputDispatcher::OutBoundQueue队列
      1.队列中的有事件消息
      2.被Notifyargs::notify()函数唤醒
  2.调用对应的InputDispatcher::dispach函数
    1.从InputDispatcher::OutBoundQueue队列中取得EventEntry对象
    2.将EventEntry对象转换成DispatchEntry对象
    3.将DispatchEntry对象加入到InputChannel::Connection::outboundQueue队列中
      【Q8】:这个InputChannel::Connection对象是从哪里来的?有什么用?
【Q9】:现在InputDispatcher得到了转换后的事件消息,即将要发出去,但是往哪里发?
<A9>:要解决这个问题,需要回到【Q2】

4.【Q2】:ViewRoot有什么用?它做了哪些事情?->{解决问题2:Android上层应用是怎么捕捉到输入事件的?
  <A2>:1.首先,ViewRoot是一个Hander,与当前Activity绑在一起的
      2.ViewRoot有一个重要的作用:与WMS通信,完成整个GUI窗口系统的绘制
      3.创建ViewRoot的时候做了哪些事情?现在来解决问题2:Android上层应用是怎么捕捉到输入事件的?

1.由前面分析知道,Android的事件输入来自InputManager,所以ViewRoot需要与InputManager通信
  【Q10】:ViewRoot怎么与InputManager通信并取得事件消息?
  <A10>:ViewRoot与InputManager之间有一个共享内存(ShareMemory
    1.InputManager::InputDispatcher将最后的事件消息发送到共享内存(ShareMemory)中
    2.ViewRoot在知道有事件消息到来时,就去共享内存(ShareMemory)中取此事件消息
  【Q11】1.ViewRoot怎么知道有事件消息到来?
  <A11>:1.ViewRoot与InputManager是通过管道通信的机制来传递消息的
    1.在创建ViewRoot后,会创建两个InputChannel类对象
      1.其中一个InputChannel对象注册到NativeInputQueue中,与ViewRoot绑在一起
        【Q12】这个NativeInputQueue是用来干什么的?
        <A12>:这个NativeInputQueue是Android系统用来维护事件接收的,因为同一时刻,会有很多Activity在等待事件输入
      2.另一个InputChannel对象注册到InputManager类对象中
    2.同时会申请上面用到的的共享内存
    3.InputChannel类主要封装管道描述符和共享内存的描述符等信息
      1.在ViewRoot与InputManager中各注册了一个InputChannel类对象,其中各有两个管道描述符
        1.两个InputChannel对象中都包含一个读和一个写描述符
        2.所以在ViewRoot与InputManager之前完成了全双工的通信【后面会用到】
  【Q13】:在Android中,每创建一个Activity,就会创建一个ViewRoot,所以也会创建一个InputChannel对象,那Android系统怎么来区分这些Activity?
  <A13>:还记得【Q8】吗,两个问题一起解决:InputChannel::Connection对象是从哪里来的?有什么用?
    1.为了区分不同的Activity,NativeInputQueue类中定义了一个子类Connection
    2.在注册InputChannel对象时,每个InputChannel对象中都创建了一个Connection对象
      1.所以ViewRoot中的每个InputChannel与InputManager中的InputChannel都包含一个Connection对象
      2.这个Connection对象标识了不同的Activity

        4.到现在我们就可以回到【Q9】了

5.【Q9】:现在InputDispatcher得到了转换后的事件消息,即将要发出去,但是往哪里发?
 <A9>:1.由上面的分析,我们知道现在需要将事件消息发送给ViewRoot
    1.调用InputChannel::Connection::inputPublisher.publishMotionEvent函数将事件消息发送到共享内存(ShareMemory)
    2.InputChannel对象向写管道发送一个dispatch信息

6.现在工作就该转移到ViewRoot这边了
  【Q14】:在NativeInputQueue中有很多InputChannel对象,究竟哪个InputChannel的管道会收到信息?
  <A14>:还记得【Q13】吗,InputManager的InputChannel对象是由ViewRoot创建后注册过去的
    1.在每个注册的InputChannel对象中,都包含了一个Connection对象
    2.InputManager的InputChannel对象的Connection对象<--->NativeInputQueue中的InputChannel对象的Connection对象,是一一对应的关系
    3.所以与InputDispatcher中Connection对象相对应的那个Connection对象将收到管道信息
  1.相应的InputChannel对象的Connection对象收到管道信息
  2.调用InputChannel::Connection::inputConsumer到共享内存(ShareMemory)中取得事件消息
  3.InputChannel再向写管道中发一个信息,表示此事件已经取得
  4.然后InputManager中的InputChannel将会收到管道消息,再继续进行下一轮事件处理

7.到此为止,事件消息已经传递到NativeInputQueue中的InputChannel对象中
  1.由前面分析,NativeInputQueue中的每个InputChannel对象都对应一个Activity
  2.NativeInputQueue中InputChannel对象是在ViewRoot创建之后创建的,也就是我们的Activity启动之前
    【Q15】:现在Activity关联的InputChannel对象拿到的这个事件消息,但是怎么处理呢?现在我们就来回答问题3:Android上层应用捕捉到输入事件是怎么响应的?
    <A15>:这个需要回到ViewRoot端InputChannel对象注册的时候,也就是【Q11】的位置
      1.ViewRoot端InputChannel对象在向NativeInputQueue注册时,需要注册3个参数
        1.其中有一个参数就是ViewRoot的成员变量InputHandler
          1.InputHandler就是事件的处理函数,也就是所谓的回调函数
          2.传递它的作用主要是明确当前ViewRoot的事件处理函数
          3.当InputChannel对象取得事件后,就会去调用ViewRoot的InputHandler函数-{到此为止,事件消息就传递回了Android应用层}
            【Q16】:这个回调函数到底是什么?
            <A17>:这个回调函数大多被Android系统实现成抽象函数
              1.在我们的电视棒远程遥控器的客户端,就重写了对应的方法,用来获取我们需要的数据,比如onScroll/onLongPress等


思维导图

【Android】Android输入子系统的更多相关文章

  1. i.mx android6 输入子系统分析(未完)

    参考:http://blog.csdn.net/u010312937/article/details/53285286 https://www.jianshu.com/p/7fca94b330ea   ...

  2. 【Android】Android输入子系统【转】

    本文转载自:https://www.cnblogs.com/lcw/p/3506110.html Linux输入子系统回顾 1:为什么要回顾linux输入子系统?这个问题后面自然就知道了 1.linu ...

  3. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

  4. Android系统--输入系统(六)模拟输入驱动程序

    Android系统--输入系统(六)模拟输入驱动程序 1. 回顾输入子系统 简单字符设备驱动:应用程序通过调用驱动所实现的函数使能硬件. 输入子系统:由于有多个应用程序使用输入子系统,故肯定使用的是早 ...

  5. Android系统--输入系统(十一)Reader线程_简单处理

    Android系统--输入系统(十一)Reader线程_简单处理 1. 引入 Reader线程主要负责三件事情 获得输入事件 简单处理 上传给Dispatch线程 InputReader.cpp vo ...

  6. android键盘输入读取

    android键盘输入读取  监控android键盘输入方式有两种,一种在java层实现,重写onKeyDown和onKeyUp方法.另一种是在jni层实现,监控/dev/input/event0键盘 ...

  7. Android系统--输入系统(五)输入系统框架

    Android系统--输入系统(五)输入系统框架 1. Android设备使用场景: 假设一个Android平板,APP功能.系统功能(开机关机.调节音量).外接设备功能(键盘.触摸屏.USB外接键盘 ...

  8. Android系统--输入系统(七)Reader_Dispatcher线程启动分析

    Android系统--输入系统(七)Reader_Dispatcher线程启动分析 1. Reader/Dispatcher的引入 对于输入系统来说,将会创建两个线程: Reader线程(读取事件) ...

  9. Android系统--输入系统(八)Reader线程_使用EventHub读取事件

    Android系统--输入系统(八)Reader线程_使用EventHub读取事件 1. Reader线程工作流程 获得事件 size_t count = mEventHub->getEvent ...

随机推荐

  1. python tkinter-窗体

    1.导入自带的包名 import tkinter 2.创建一个窗体对象 form=Tkinter.Tk() 3.显示窗体(这句应该是所有的设置部署完最后执行的一句代码) form.mainloop() ...

  2. python中使用XPath笔记

    XPath在Python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线. XPath介绍: ...

  3. hdu 3068 最长回文【manacher】(模板题)

    <题目链接> 最长回文 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.回文就是正反读都是一样的字符串,如ab ...

  4. 李宏毅机器学习笔记1:Regression、Error

    李宏毅老师的机器学习课程和吴恩达老师的机器学习课程都是都是ML和DL非常好的入门资料,在YouTube.网易云课堂.B站都能观看到相应的课程视频,接下来这一系列的博客我都将记录老师上课的笔记以及自己对 ...

  5. Centos7 安装pyquery 等包的简易方法

      单独下载安装模块: sudo mkdir /home/pythonmodule sudo wget https://www.crummy.com/software/BeautifulSoup/bs ...

  6. 一个新的Android Studio 2.3.3可以在稳定的频道中使用。A new Android Studio 2.3.3 is available in the stable channel.

    作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.com 一个新的Android Studio 2.3 ...

  7. safari 收藏导出 手机safari 导出

    safari 收藏导出 手机safari 导出 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.co ...

  8. C# OpenFileDialog打开文件对话框(详解)

    一.打开文件对话框(OpenFileDialog) 1. OpenFileDialog控件的基本属性 InitialDirectory:对话框的初始目录 Filter: 获取或设置当前文件名筛选器字符 ...

  9. unity中HideFlags的利用

    HideFlags主要用于控制物体在hierarchy,Inspector视图的显示销毁等的bit mask~ None:默认情况,正常可见对象~ API: HideInHierarchy:在Hier ...

  10. 喵哈哈村的魔法考试 Round #8 (Div.2) 题解

    喵哈哈村的美食面馆 签到题,就不停的if就好了. #include<bits/stdc++.h> using namespace std; string name[5]={"ni ...