全部博文(207)
分类: Android平台
2016-03-30 16:59:18
1.基本流程:
./表示android源代码目录,按键属于输入设备,支持5种输入设备:触摸屏 GPIOkey usb键盘 usb鼠标 powerkey。
android按键消息通过二次转换:将驱动report上来的SCANCODE(include/linux/input.h中),通过文
件*.kl(默认为qwerty.kl)的映射,得到KEYCODE字串;通过二维表static const KeycodeLabel
KEYCODES[],将上面的字符串转换成android
需要的键值信息(frameworks\base\libs\ui中的KeyLayoutMap.cpp)。如果是输入键,还需要查询keymap,得到
相应的字符。
KEYLAYOUT 按键布局
文件:*.kl(默认为qwerty.kl)
文件格式:
key SCANCODE KEYCODE [FLAGS...]
第一列:key
第二列: SCANCODE是一个整数,是驱动里面定义的,在文件./kernel/include/linux/input.h
第三列: KEYCODE 是一个字串,定义在你描述的布局文件frameworks/native/include/input/KeycodeLabels.h
另外可以设置相关的FLAGS:
SHIFT: 当按下,自动加上SHIFT键值
ALT:当按下,自动加上ALT
CAPS:当按下,自动带上CAPS大写
WAKE:当按下,当设备进入睡眠的时候,按下这个键将唤醒,而且发送消息给应用层。
WAKE_DROPPED:当按下,且设备正处于睡眠,设备被唤醒,但是不发送消息给应用层。
接下来自下而上说明:
*驱动程序:./kernel/drivers/**/input(/saradc为例)目录。负责report键值到上层,其中键值定义在 input.h中;驱动程序中会定义Device->name,如IR定义的设备名称为aml_keypad,它决定了根文件系统的按键布局或按键 字符映射;获取设备名字:函数为int EventHub::pen_device(const char *deviceName),打开键盘设备的时候通过上面的 ioctl 获得设备名称,命令字 EVIOCGNAME 的定义在文件: kernel/include/linux/input.h 中。 #define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */。
*根文件系统中:KeyLayout(按键布局)和KeyCharacterMap(按键字符映射),后缀名称分别为kl和kcm;如果使用USB键盘, 则使用/system/usr/keylayout/qwerty.kl,定制系统一般放在./device/usr/keylayout /qwerty.kl目录下,如Amlogic使用SAR口做按键,则使用./device/**/adc_keypad.kl,IR按键,则使用. /device/**/aml_keypad.kl;获取此文件方法:函数为int EventHub::openDevice(const char *deviceName){
....
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
bool defaultKeymap = false;
if (access(keylayoutFilename, R_OK)) {
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
....
}
如果没有定义键盘映射文件,那么默认使用系统的 /system/usr/keylayout/qwerty.kl 可以修改./system/usr/keylayout/qwerty.kl 文件改变Android公司的按键映射,定制系统一般放在./device/**/keylayout/qwerty.kl。
*EventHub: libui的一部分,实现了对驱动程序的控制。目录 ./frameworks/base/services/input/,读取RawEvent事件。
*Java框架层的处理:有KeyInputDevice等类来处理EventHub传递上来的信息,这些信息通过RawInputEvent和
KeyEvent来表示。一般情况下,对于按键事件,以后者的形式传送给应用程序,而触摸屏和轨迹球事件以前者的形式转换形成MotionEvent事件
传送给应用程序;
*Android应用程序层:通过重载onKeyDown()和onkeyUp()等方法接收KeyEvent(按键事件),通过重载onTouchEvent()和onTrackballEvent()等方法接收MotionEvent(运动事件);
*总之:驱动事件值--->key值(如KEY_STOP在./kernel/include/linux /input.h)--->KEYCODElable名称值(如MEDIA_STOP在./frameworks/native/include/input/KeycodeLabels.h)--->KEYCODE值(如KEYCODE_MEDIA_STOP在./frameworks/base /core/java/android/view/KeyEvent.java)--->onKeyDown()或onkeyUp()功能处理。
2.按键功能修改:
从上面总之的流程中可知,修改按键做法比较简单,只要每个key的按键功能正常的话,只需更换驱动事件对应的key值,故我们先检查整个流程是否打通,打通的话就是改两端而已,要么在onKeyDown()或onkeyUp()上加功能而已。
3.按键功能添加:
对keypad来说,以SAR为例涉及到的有以下几个:
1.首先在*.kl文件中,添加新的键值信息:Example: key 100 MEDIA_PLAY_PAUSE ,把100定义到input.h中#define KEY_PLAYPAUSE 100
注意:新加的键值不要与已有的重复。
2. ./kernel/drivers/**/input/Adc_keypad.c驱动对应的board-8726m-refc03.c中的
adc_kp_key[]对应的键位添加自定义的键码 如 :{KEY_PLAYPAUSE , "playpause", CHAN_5, 419, 60},
3. ./frameworks/base/include/ui/keycodeLabels.h
在数组static const KeycodeLabel KEYCODES[] 中添加 新定义的信息
{ "PLAYPAUSE", 120 },
4. ./ frameworks/native/include/android/keycodes.h
枚举类型 中添加
AKEYCODE_PLAYPAUSE = 120
5. ./frameworks/base/core/res/res/values/attrs.xml
6. ./frameworks/base/core/java/android/view/KeyEvent.java
public static final int KEYCODE_PLAYPAUSE = 120;
注意:要修改为 private static final int LAST_KEYCODE = KEYCODE_PLAYPAUSE;
7. ./frameworks/native/libs/input/input.cpp
在bool KeyEvent::isSystemKey(int32_t keyCode)()中,同样需要添加:
case AKEYCODE_PLAYPAUSE:
8. 通过以上的更改,新的键值就添加上去了,注意上面标红色的数字表示必须相同的,另外由于更改了 KeyEvent,影响到了API, 所以需要make update-api
如果对新键值进行处理,可以通过获取相应的keycode,对它进行处理;对于按键事件的处理一般如下文件中
frameworks/policies/base/phone/com/android/internal/policy/impl
/PhoneWindowManager.java,综上可知,我们可以根据需求定义自己的键值,并对键值所对应的事件信息进行合理化处理。
补充: 在写程序时,需要捕获KEYCODE_HOME、KEYCODE_ENDCALL、KEYCODE_POWER这几个按键,但是这几个按键系统做了特殊处 理,在进行dispatch之前做了一些操作,HOME除了Keygaurd之外,不分发给任何其他APP,ENDCALL和POWER也类似,应用程序 在View和Activity的onKeyDown/Up中是监听不到的,所以需要我们系统处理之前进行处理,我的做法是自己定义一个FLAG,在自己的 程序中添加此FLAG,然后在WindowManagerServices.java中获取当前窗口的FLAG属性,如果是我们自己设置的那个FLAG, 则不进行特殊处理,直接分发按键消息到我们的APP当中,由APP自己处理。这部分代码最好添加在
@Override
boolean preprocessEvent(InputDevice device, RawInputEvent event)方法中,这个方法是KeyInputQueue中的一个虚函数,在处理按键事件之前的一个“预处理”。
注意:对HOME键的处理好像必需要修改PhoneWindowManager.java中的interceptKeyTi方法,具体可以参考对KeyGuard程序的处理。
4.参考资料下载地址:
5.相关兄弟文章