android系统按键从linux到java流程
更新时间:2024-01-09 22:14:01 阅读量: 教育文库 文档下载
android系统按键从linux到java流程
概述:
android系统的键值转换,从linux到java共经历3个层次,分别是:
1 linux系统层,原始ir键值读取,转变为linux层键值 2 framework层,linux层键值转换为android键值 3 framework层,android键值上报java层 关系图: InputManager InputReaderThread InputDispatcherThreagetevents() mapkey() framework层: linux层键值转换为android键值 interceptKeyBeforeQueueing() interceptKeyBeforeDispatch ing() linux层: 转变为linux层键值 dev/input 原始ir键值读取,dev/vinput 最终输出:framework层: phonewindowmanger.java framework层: android键值上报java层
剩余疑问:dev/vinput和dev/input之间到底怎么关联的
间造成数据异常;二者由系统负责同步。 后续查到相关资料再补充.
目前没查到这二者是怎么关联的,但是从用户组可以看出,vinput应该是input的输入。 猜测:这二者之间类似一个软连接处理,避免键值读写都在同一个文件,在不同进程之
1 原始ir键值读取,转变为linux层键值 核心输入: 系统启动android_ir_user后台进程 核心输出: 虚拟设备dev/vinput,写入linux键值
1.1 流程图
key.xml:定义原始硬件码值和对应linux层通用键名: < key value =\name=\UTE\> linux_key.h: 定义linux层通用键名和linux层键值的对应关系 {\13} \桥梁 key_pars.c:提供 get_keycode接口,解析key.xml,结合linux_key.h,建立原始码值和linux层键值的映射数组 调用get_keycode 输出dev/vinput: 读取原始码值,根据keyarry转换为linux层键值,通过ReportKeyEvent写入虚拟设备dev/vinput设备,其他进程再从这读取linux键值 系统启动android_ir_user后台进程,执行ir_user.cpp的main入口 keyarry键值全局数组 原始键读取线程ir_sample_thread keyarry:桥梁
1.2转换流程详细说明:
1.2.1/device/hisilicon/bigfish/system/ir_user/key_pars/key.xml: 该文件编译后输出到/etc目录下。
定义原始硬件码值和对应linux层通用键名:
< key value=\
1.2.2/device/hisilicon/bigfish/system/ir_user/key_pars/linux_key.h: 定义linux层通用键名和linux层键值的对应关系: {\
1.2.3/device/hisilicon/bigfish/system/ir_user/key_pars/ key_pars.c: 提供接口get_keycode解析xml建立原始码值和linux层键值的映射数组 get_keycode(_key_arry *keyarry, int keyarry_num)解析: a:调用readXml读取/etc/ key.xml,返回xml根节点:
key.xml中
b:调用Pars_Key解析每个节点下的所有按键:
解析节点下的所有children节点,通过value属性获取xml定义的原始键值,通过name属性获取xml定义的键名字符串,再调用find_linux_key_code查询这个字符串对应的linux层键值。
把这个映射关系存储到:
keyarry->hi_keycode[keynum].ir_keycode = ir_keycode; keyarry->hi_keycode[keynum].linux_keycode = linux_keycode;
c: find_linux_key_code函数遍历在linux_key.h定义的Linux_KeyCode_Ary数组,找到参数str(即键名字符串)对应的linux层键值。
1.2.4/device/hisilicon/bigfish/system/ir_user/ir_user.cpp: 1)编译为后台进程android_ir_user独立运行;
2)进程启动,执行main入口函数,调用get_keycode完成key.xml存储原始码值和linux层键值的映射关系到数组keyarry;
3)建立ir_sample_thread线程读取原始码值,根据keyarry转换为linux层键值,通过ReportKeyEvent写入dev/vinput设备,其他进程可以读取键值 1.3总结
出差拷机,如果不想其他厂家操作我们的盒子,只需要离开的时候删除/system/bin下面的android_ir_user这个bin文件即可。
113 },
结构linux_keycode_ary类型的数组Linux_KeyCode_Ary[512].
2 linux层键值转换为android键值
核心输入: /dev/input, 输入linux键值
核心输出: 1)getEvents(),输出RawEvent* buffer,存储linux层键值; 2)mapKey(),将linux层键值转换为android键值
2.1流程图
getEvents():,输出RawEvent* buffer mapKey(): 将linux层键值转换为android键值
Vendor_0001_Product _0001.kl: 定义linux键值和对应的键名字符串 key 113 MUTE KeycodeLabels.h: 定义键名字符串和android的java层键值对应关系结构数组KEYCODES: { \Keyboard.cpp: 定位linux到android键第一转换映射文件为Vendor_0001_Product_0001.kl KeyLayoutMap.cpp: 1)解析Vendor_0001_Product_0001.kl,并结合KeycodeLabels.h最终建立里建立linux层键值到android键值 的映射集合map 2)提供mapKey EventHub.cpp: 1 getEvents() 2 mapKey() 1)建立linux层键值到android键值 2)读取/dev/input设备上的 的映射集合map 键值到RawEvent* buffer 2.2详细说明
2.2.1 /device/hisilicon/bigfish/prebuilts/Vendor_0001_Product_0001.kl: 定义linux键值和对应的键名字符串 key 113 MUTE
2.2.2 /frameworks/native/include/input/KeycodeLabels.h
定义键名字符串和android的java层键值对应关系结构数组KEYCODES: { \
2.2.3\\frameworks\\native\\libs\\input\\ Keyboard.cpp
提供函数loadKeyLayout和getPath,定位linux到android键第一转换映射文件为Vendor_0001_Product_0001.kl:
提供函数getKeyCodeByLabel,从KeycodeLabels.h的KEYCODES数组中,查询返回android的java键值。
2.2.4\\frameworks\\native\\libs\\input\\ KeyLayoutMap.cpp
提供函数load和parse等解析Vendor_0001_Product_0001.kl,并调用
Keyboard.cpp的getKeyCodeByLabel,通过“linux键值---键名字符串---android的java层键值”的直接建立“linux键值--- android的java层键值”map集合; 提供函数mapKey基于此集合供上层转换linux键值为android的java键值
2.2.5 \\frameworks\\base\\services\\input\\EventHub.cpp
1 提供getEvents,完成两个事:
1):建立linux键值--- android的java层键值”map集合。 2):扫描linux键值,存储到参数RawEvent* buffer。
2 提供mapKey,调用KeyLayoutMap.cpp的mapKey函数,完成转换linux
键值为android的java键值。
2.2.6关于建立linux键值--- android的java层键值”map集合,代码流程: 1)\\frameworks\\base\\services\\input\\EventHub.cpp a)getEvents()-> scanDevicesLocked():
b)scanDevicesLocked-> scanDirLocked(): static const char *DEVICE_PATH = \
c)scanDirLocked()->openDeviceLocked()
d) openDeviceLocked()->loadKeyMapLocked()
e) loadKeyMapLocked调用Keyboard.cpp下load()函数:
2)\\frameworks\\native\\libs\\input\\ Keyboard.cpp f) load()->probeKeyMap():
g) probeKeyMap ()->loadKeyLayout():
h) loadKeyLayout()->getPath():
这里才确定path为:./system/usr/keylayout/Vendor_0001_Product_0001.kl 再继续调用KeyLayoutMap::load加载并解析
3) \\frameworks\\native\\libs\\input\\ KeyLayoutMap.cpp i) load()->parse()
j) parse ()->parseKey()
解析Vendor_0001_Product_0001.kl得到linux层code,
再调用getKeyCodeByLabel查询code对应的android层键值keyCode
将两个键值存入map集合
到此完成linux层键值到android的java层键值的转换并存储为map集合;后面真正扫描到 linux键值后,调用mapKey根据存储的map集成转换为android键值进行上报。 2.3总结:
这个部分可以看出,其完全是个中间过程,如果我们要修改某个键在android的表现,完全不用改这其中相关的任何文件,只需要在java层(后面会讲到,keyevent.java)对收到的android键进行再转换即可。
3 Android键值上报java层
核心输入 : eventhub.cpp的getEvents()和mapkey()
核心输出:PhoneWindowManager.java的interceptKeyBeforeQueueing和 interceptKeyBeforeDispatching
3.1流程图
太多,省略....
3.2详细说明
3.2.1\\frameworks\\base\\services\\input\\InputReader.cpp 该文件主要负责: linux按键的读取; 转换为android键值;
通过listener启动按键上报,注意这里仅仅是启动按键上报。 1)InputReader 类构造函数InputReader
两个极其重要的参数:
eventHub和listener,前者用来读取linux键值,后者用来分发上报按键。
说明:
参数eventHub,赋值给变量mEventHub;listener作为参数创建了mQueuedListener。 2) loopOnce函数及其调用的一连串函数
调用mEventHub->getEvents读取linux按键到RawEvent 类型的mEventBuffer成员变量;
调用processEventsLocked分发上报。
3)重点阐述processEventsLocked是如何分发上报按键的: a)processEventsLocked调用processEventsForDeviceLocked
b) processEventsForDeviceLocked从InputReader 的(InputDevice类型)变量mDevices变量中找出当前对应的device,调用其process
c) process调用InputDevice的变量(InputMapper类型)mMappers的process函数
d)这里的mapper是KeyboardInputMapper类
这里process开始调用eventhub类的mapKey()启动参数rawEvent中的linux键值scanCode的转换,变为android键值keyCode,继续调用processKey上传.
e) processKey调用 getListener()->notifyKey(&args); 完成键值上报到其他模块
注意这里的getListener就是取得inputreader类初始化时用参数“const sp
到这里,inputread.cpp模块的按键分发上报全部完成,后续只要搞清楚
sp
4)阐述按键分发中的关键衔接一环:
上述C)步的mMappers是如何和KeyboardInputMapper关联的? 为什么KeyboardInputMapper的getListener()是inputreader的mQueuedListener?
仍然是从loopOnce 调用processEventsLocked开始:
a) processEventsLocked找不到按键rawEvents的设备来源时调用addDeviceLocked:
b) addDeviceLocked调用createDeviceLocked:
(所以上面第3)点钟的B)步能找到有效的device) c) createDeviceLocked调用device->addMapper
到这里就完成了inputreadr----inputdevice---mappers的关联.
getListener的来源:
InputReader构造函数用参数sp
createDeviceLocked在 添加inputdevice设备时,使用mContext转换为InputReaderContext作为参数创建了device,直接赋值给inpudevice的成员变量mContext。
createDeviceLocked继续addMapper创建了KeyboardInputMapper实例,即(new KeyboardInputMapper(device, keyboardSource, keyboardType)); device用于创建InputMapper对象,device的mContext成员赋值给InputMapper的mContext;KeyboardInputMapper又继承自InputMapper。
所以KeyboardInputMapper的成员mContext等价于InputReader的mContext;getListener等价于调用InputReader的mContext->getListener即mQueuedListener。 5)InputReaderThread线程类
构造函数InputReaderThread使用InputReader 类对象作为参数,赋值给变量mReader。
threadLoop方法中调用mReader->loopOnce();
只要创建了InputReader实例,并以此启动InputReaderThread线程,就会反复mEventHub->getEvents读键和调用processEventsLocked分发上报。
3.2.2\\frameworks\\base\\services\\input\\InputDispatcher.cpp
该文件是真正的按键数据上报的起点,InputReader中的listener实际就是这个InputDispatcher,该类继承自接口InputDispatcherInterface。 完成3个事情:
interceptKeyBeforeQueueing,按键入列前的截获处理
interceptKeyBeforeDispatching,按键从队列分发上层app前的截获处理 startDispatchCycleLocked,完成按键最终分发上报 1)InputDispatcher类
a)构造函数以参数policy直接初始化成员变量mpolicy。
b) notifyKey函数被之前的inputreader调用,notifyKey调用interceptKeyBeforeQueueing
进行第一次按键上报,供上层截获按键,在按键被压入队列前进行必要的处理,如设置,桌面等在keyfuntion.xml中配置的快捷键,或待机键等的处理。
到这里完成按键进入Queue队列前的上报处理,如果interceptKeyBeforeQueueing没有处理这个按键,将会继续调用步骤c)。
c) enqueueInboundEventLocked将按键结构参数entry压入队列
d) InputDispatcherThread线程类不断循环,检测到队列中的按键 InputDispatcherThread调用InputDispatcher的dispatchOnce。
e) dispatchOnce调用dispatchOnceInnerLocked
f) dispatchOnceInnerLocked调用dispatchKeyLocked
g) dispatchKeyLocked调用
doInterceptKeyBeforeDispatchingLockedInterruptible
h) doInterceptKeyBeforeDispatchingLockedInterruptible调用 mPolicy->interceptKeyBeforeDispatching
到这里完成进入队列的按键在上报app前的截获处理,比如特殊处理home键,添加工装菜单的按键调用等,如果这些按键仍然没被截获,将继续进行分发,继续下面的步骤i). i) dispatchKeyLocked调用addMonitoringTargetsLocked,获取所有上层注册过需要按键的channel,创建目标通道集合inputTargets; 继续调用dispatchEventLocked开始按键分发。
j) dispatchEventLocked对每一个通道获取其connection,再调用prepareDispatchCycleLocked
k) prepareDispatchCycleLocked调用enqueueDispatchEntriesLocked l) enqueueDispatchEntriesLocked调用enqueueDispatchEntryLocked把按键eventEntry和目标通道inputTarget写入connection 再调用startDispatchCycleLocked开始分发
m) startDispatchCycleLocked调用connection->inputPublisher.publishKeyEvent完成最终分发按键。
2)InputDispatcherThread线程类
构造函数InputDispatcherThread使用InputDispatcherInterface类对象作为参数,赋值给变量mDispatcher。
threadLoop方法中调用mDispatcher->dispatchOnce();
只要创建了mDispatcher实例,并以此启动InputReaderThread线程,就会反复mDispatcher->dispatchOnce(),完成按键分发。
3.2.3\\ frameworks\\base\\services\\input\\InputManager.cpp
该文件负责启动前面阐述的InputReader和InputDispatcher。
1)创建InputReader和InputDispatcher实例,初始化到mDispatcher和mReader。
2)创建InputReaderThread和InputDispatcherThread两个线程
3)启动线程循环运行start
3.2.4 \\frameworks\\base\\services\\jni\\
com_android_server_input_InputManagerService.cpp (InputManagerService.java的JNI)
1)nativeInit()调用NativeInputManager(),后者调用new
InputManager(eventHub, this, this);完成创建InputManager对象的实例mInputManager。
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
NativeInputManager 是多重继承,其父类之一为虚类自己;
InputDispatcherPolicyInterface,所以最后一个this参数实际就是NativeInputManager InputManager运行InputDispatcher,后者调用的interceptKeyBeforeQueueing和
interceptKeyBeforeDispatching 实际就是NativeInputManager的同名函数。
也就是说从JNI初始化InputManager,启动InputReader和InputDispatcher,而InputDispatcher最终回调JNI的interceptKeyBeforeQueueing和
interceptKeyBeforeDispatching完成按键截获处理。
2)nativeStart调用mInputManager的start启动两个线程InputReaderThread
和InputDispatcherThread.
3) NativeInputManager的interceptKeyBeforeQueueing调用java层interceptKeyBeforeQueueing
4)NativeInputManager的interceptKeyBeforeDispatching调用java层interceptKeyBeforeDispatching
5)register_android_server_InputManager将interceptKeyBeforeQueueing和interceptKeyBeforeDispatching,与InputManagerService.java的同名函数进行关联
3.2.5 \\frameworks\\base\\services\\java\\com\\android\\server\\input
\\InputManagerService.java:
1)构造函数InputManagerService())调用JNI函数nativeInit();
2)interceptKeyBeforeQueueing()调用WindowManagerCallbacks的同理。
interceptKeyBeforeQueueing方法继续上抛按键; interceptKeyBeforeDispatching
通过setWindowManagerCallbacks()方法设置。
3)WindowManagerCallbacks是内部类,mWindowManagerCallbacks是成员变量,
4)start方法调用nativeStart,这里实现调用InputManager的start,启动mDispatcherThread和mReaderThread两个线程。
3.2.6 \\frameworks\\base\\services\\java\\com\\android\\server\\ SystemServer.java initAndLoop ()启动InputManagerService
这里的setWindowManagerCallbacks设置的callback,即WindowManagerService类的getInputMonitor方法
至此,底层的InputReaderThread和InputDispatcherThread全部启动完毕,现在的重点是要找到WindowManagerCallbacks的interceptKeyBeforeQueueing和interceptKeyBeforeDispatching最终指向了哪里,由于两个方法网上的映射机制是一样的,下面只讲interceptKeyBeforeQueueing。 继续往下走。
3.2.7\\ frameworks\\base\\services\\java\\com\\android\\server\\wm \\WindowManagerService.java
////////////////////////////////////////////////////////////////////////////////////// InputMonitor的创建:
////////////////////////////////////////////////////////////////////////////////////// getInputMonitor直接返回InputMonitor:
////////////////////////////////////////////////////////////////////////////////////// 创建mPolicy:
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
3.2.8 \\frameworks\\base\\services\\java\\com\\android\\server\\wm\\
InputMonitor.java:
////////////////////////////////////////////////////////////////////////////////////// 该类继承自InputManagerService 的内部类WindowManagerCallbacks接口。 ////////////////////////////////////////////////////////////////////////////////////// interceptKeyBeforeQueueing()函数被重载,并调用调用WindowManagerPolicy接口的interceptKeyBeforeQueueing。
////////////////////////////////////////////////////////////////////////////////////// 该service由构造函数传入,实际就是WindowManagerService:
mPolicy由PolicyManager类的makeNewWindowManager()创建。
3.2.9 \\frameworks\\base\\core\\java\\com\\android\\internal\\policy\\ PolicyManager.java
////////////////////////////////////////////////////////////////////////////////////// makeNewWindowManager调用Policy.java的方法 makeNewWindowManager()
////////////////////////////////////////////////////////////////////////////////////// sPolicy的初始化:
ava
3.2.10\\frameworks\\base\\policy\\src\\com\\android\\internal\\policy\\impl\\Policy.jmakeNewWindowManager调用TVWindowManager.java类的构造函数创建对象实例。
dowManager.java
3.2.11\\frameworks\\base\\policy\\src\\com\\android\\internal\\policy\\impl\\TVWinTVWindowManager 类自身没有interceptKeyBeforeQueueing方法,但其继承自PhoneWindowManager类
3.2.12\\frameworks\\base\\policy\\src\\com\\android\\internal\\policy\\impl\\ PhoneWindowManager.java
PhoneWindowManager.java具备最终形态的interceptKeyBeforeQueueing和interceptKeyBeforeDispatching,到此流程走完。
3.3总结
3.2.5中的步骤4)中setWindowManagerCallbacks实际就是创建并设置了PhoneWindowManager类的实例,InputManagerService实现了调用PhoneWindowManager的interceptKeyBeforeQueueing和interceptKeyBeforeDispatching。
总结:
回头看整个按键转换和分发流程,有两个根节点:
1 第一层次读取linux键值的进程,android_ir_user这个bin文件,由系统脚本开机拉起 2 第二、三层次的按键分发,总源头为SystemServer这个服务的启动,由android系统开机启动,详细流程参见< android SystemServer 启动流程>.
以上除了第一层次linux键值的获取外,其他所有层次实际都是一个通用的转换和上报过程,最终到达PhoneWindowManager的interceptKeyBeforeQueueing和interceptKeyBeforeDispatching后,按键是以KeyEvent类实例作为参数传入的,并且必须以getKeyCode来访问实际android键值。 所以:
1)如果系统有增加按键的需求,需要修改:
/device/hisilicon/bigfish/system/ir_user/key_pars/key.xml /device/hisilicon/bigfish/system/ir_user/key_pars/linux_key.h /device/hisilicon/bigfish/prebuilts/Vendor_0001_Product_0001.kl /frameworks/native/include/input/KeycodeLabels.h /frameworks/base/core/java/android/view/KeyEvent.java
以上文件一一添加,只要最终KeycodeLabels.h和KeyEvent.java中定义的android键是正确的,前面其他文件所有定义只需要能保持一一对应即可。 另外
/frameworks/base/core/res/res/value/attrs.xml这个文件的作用是给上层APP用的一个资源文件,这个文件如果实际没人用,是不需要修改的;当然为防万一,在增加按键的时候还是把这个同步。
2)如果项目只是需要修改某个键值,比如映射为其他键等,可以直接修改KeyEvent.java这个类的getKeyCode方法即可,其他都不需要修改。
比如
正在阅读:
避暑山庄策划书11-01
仅仅觉知是不够的 德加尼亚禅师04-29
Lqziaq公务员面试经验和整理的100个经典题目07-08
啤酒基础知识08-25
啤酒沸水处理工艺开题报告10-25
中国公共政策的公众议程设置模式的探讨06-20
石家庄精英中学02-09
仁爱版 - 八年级上学期必考的12篇英语作文,这里全啦!04-06
商学精学08-21
- exercise2
- 铅锌矿详查地质设计 - 图文
- 厨余垃圾、餐厨垃圾堆肥系统设计方案
- 陈明珠开题报告
- 化工原理精选例题
- 政府形象宣传册营销案例
- 小学一至三年级语文阅读专项练习题
- 2014.民诉 期末考试 复习题
- 巅峰智业 - 做好顶层设计对建设城市的重要意义
- (三起)冀教版三年级英语上册Unit4 Lesson24练习题及答案
- 2017年实心轮胎现状及发展趋势分析(目录)
- 基于GIS的农用地定级技术研究定稿
- 2017-2022年中国医疗保健市场调查与市场前景预测报告(目录) - 图文
- 作业
- OFDM技术仿真(MATLAB代码) - 图文
- Android工程师笔试题及答案
- 生命密码联合密码
- 空间地上权若干法律问题探究
- 江苏学业水平测试《机械基础》模拟试题
- 选课走班实施方案
- 按键
- 流程
- android
- 系统
- linux
- java
- 高考化学一轮复习 考点突击一 阿伏伽德罗常数常数的正误判断(精练)
- 第二十八章 中考重点热点精练 - 图文
- 七年级(上)数学错题集2
- 中国财产保险行业运营态势报告(2013-2018)
- 鄂教版三年级上学期语文归类复习1
- 老旧小区管网改造技术要求2012
- 人有9种体质老中医教你怎样自测体质及应对方式
- 2018中考语文复习全国各地中考语文试卷:记叙文阅读汇编(答案解析)
- VOASpecialEnglish
- 菜篮子项目电子商务创业计划书
- 南开现代远程学习概论-作业
- 物理第一单元
- 基层党建工作存在问题与对策探讨
- 云南省科技计划项目管理办法
- 七年级最新地理教案-七年级地理天气与气候1001 精品
- 2016年临床执业医师综合笔试模拟试题及答案(十二)
- 2012实验专题
- 填空题的求解思路、方法与技巧,非常全
- 大润发关于供应链的案例
- 射频 rc500代码完全注释