Android物理按键功能更改

这两天有个需求,需要将Android手机上的音量 +/- 按键更改为以前功能机的上/下按键。一头雾水的我开始百度如何更改Android手机的物理按键功能。经过一番查询和实际演练终于完成了需求,下面将该过程进行总结。
(PS: 由于我只是做android应用相关的,对驱动层的知识懂的不多,这里仅仅是记录一下我解决问题的方法,如果要深究Android的按键流程,还是要参考其他资料。)

首先,我们需要搞清楚的是,用户点击了一个按钮后究竟发生了什么。这就不得不说一下Android的INPUT子系统了。其精简流程图如下:

Android 修改按键音 安卓按键更改_Android


根据流程图,从下往上看,首先是Linux内核层上报按键码,我在这里引用简书作者天空汁橙在《Android物理按键输入事件(一)》中所写来描述Linux内核层的操作:

① linux内核通过扫码,将硬件上按键按下电压变换为数字电压并映射到数组下标;

② 通过数组关系转化将按键映射到内核中预定义的键值;

③ 最终linux内核上报给上层的就是这个整型值。

其中,这些上报的值反映在kernel/uapi/linux/input.h中。

//input.h部分代码
... ...
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_RIGHTALT 100
#define KEY_LINEFEED 101
#define KEY_HOME 102
#define KEY_UP 103
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_PAGEUP 104
#define KEY_LEFT 105
#define KEY_RIGHT 106
#define KEY_END 107
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_DOWN 108
#define KEY_PAGEDOWN 109
#define KEY_INSERT 110
#define KEY_DELETE 111
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_MACRO 112
#define KEY_MUTE 113
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_POWER 116
#define KEY_KPEQUAL 117
#define KEY_KPPLUSMINUS 118
#define KEY_PAUSE 119
... ...

//input.h部分代码
... ...
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_RIGHTALT 100
#define KEY_LINEFEED 101
#define KEY_HOME 102
#define KEY_UP 103
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_PAGEUP 104
#define KEY_LEFT 105
#define KEY_RIGHT 106
#define KEY_END 107
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_DOWN 108
#define KEY_PAGEDOWN 109
#define KEY_INSERT 110
#define KEY_DELETE 111
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_MACRO 112
#define KEY_MUTE 113
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define KEY_POWER 116
#define KEY_KPEQUAL 117
#define KEY_KPPLUSMINUS 118
#define KEY_PAUSE 119
... ...

那么,这些linux内核上报按键值后又会发生什么?这里就需要提到kl文件了。Android kl文件是标准linux与android的键值映射文件,kl文件可以有很多个,有关kl文件的介绍

我们可以通过以下命令查看手机所使用的kl文件有哪些:

> cat /proc/bus/input/devices

> cat /proc/bus/input/devices

输入命令后,会出现下图这样的输出:

Android 修改按键音 安卓按键更改_Android_02


从图中我们可以确定终端目前所使用的kl文件(有些kl文件找不到)。我找到了“Name=”comip_gpio_keys””的kl文件,将其打开,我们可以查看到类似以下内容:

key 113   MUTE
key 115   VOLUME_UP
key 114   VOLUME_DOWN
key 163   MEDIA_NEXT
key 164   MEDIA_PLAY_PAUSE
key 165   MEDIA_PREVIOUS
key 226   HEADSETHOOK
key 158   BACK
key 28    DPAD_CENTER
key 231   CALL
key 107   ENDCALL
key 103   DPAD_UP
key 108   DPAD_DOWN
key 105   DPAD_LEFT
key 106   DPAD_RIGHT
key 59    F1
key 60    F2


key 113   MUTE
key 115   VOLUME_UP
key 114   VOLUME_DOWN
key 163   MEDIA_NEXT
key 164   MEDIA_PLAY_PAUSE
key 165   MEDIA_PREVIOUS
key 226   HEADSETHOOK
key 158   BACK
key 28    DPAD_CENTER
key 231   CALL
key 107   ENDCALL
key 103   DPAD_UP
key 108   DPAD_DOWN
key 105   DPAD_LEFT
key 106   DPAD_RIGHT
key 59    F1
key 60    F2

kl文件中,key后面的数字就是kernel上报的按键码,后面的字符标签就是该按键码对应的android中的按键标签,我们可以看到上面“103”对应的是物理按键中的“上”,“108”对应物理按键中的“下”。当用户按下按键后,kernel会上报对应按键的按键码,例如我们按下音量+键,kernel就会上报按键码115,然后上层根据正确的kl文件中的对应关系,将按键对应到上层的VOLUME_UP标签上来。

那这些按键标签是哪里来的呢?
其实,这些标签也都对应一个按键码,与kernel上报的按键码不同,按键标签所对应的按键码就是我们在上层代码逻辑中使用的按键码。我们可以在frameworks/native/include/input/InputEventLabels.h中看到:这里通过宏定义将标签字符与上层按键码对应起来,其中上层按键码又是通过frameworks/native/include/android/Keycodes.h中枚举的。我们在上层所使用的按键码就是这个枚举类型中所列出的整型值。

//InputEventLabels.h部分代码
#include <input/Input.h>
#include <android/keycodes.h>

#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
… …

struct InputEventLabel {
    const char *literal;
    int value;
};

static const InputEventLabel KEYCODES[] = {
    // NOTE: If you add a new keycode here you must also add it to several other files.
    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
    DEFINE_KEYCODE(UNKNOWN),
    DEFINE_KEYCODE(SOFT_LEFT),
    DEFINE_KEYCODE(SOFT_RIGHT),
    DEFINE_KEYCODE(HOME),
    DEFINE_KEYCODE(BACK),
    DEFINE_KEYCODE(CALL),
    … …
    DEFINE_KEYCODE(POUND),
    DEFINE_KEYCODE(DPAD_UP),
    DEFINE_KEYCODE(DPAD_DOWN),
    DEFINE_KEYCODE(DPAD_LEFT),
    DEFINE_KEYCODE(DPAD_RIGHT),
    DEFINE_KEYCODE(DPAD_CENTER),
    DEFINE_KEYCODE(VOLUME_UP),
    DEFINE_KEYCODE(VOLUME_DOWN),
    DEFINE_KEYCODE(POWER),
    DEFINE_KEYCODE(CAMERA),
    DEFINE_KEYCODE(CLEAR),
    … …

    { NULL, 0 }
};

//InputEventLabels.h部分代码
#include <input/Input.h>
#include <android/keycodes.h>

#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
… …

struct InputEventLabel {
    const char *literal;
    int value;
};

static const InputEventLabel KEYCODES[] = {
    // NOTE: If you add a new keycode here you must also add it to several other files.
    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
    DEFINE_KEYCODE(UNKNOWN),
    DEFINE_KEYCODE(SOFT_LEFT),
    DEFINE_KEYCODE(SOFT_RIGHT),
    DEFINE_KEYCODE(HOME),
    DEFINE_KEYCODE(BACK),
    DEFINE_KEYCODE(CALL),
    … …
    DEFINE_KEYCODE(POUND),
    DEFINE_KEYCODE(DPAD_UP),
    DEFINE_KEYCODE(DPAD_DOWN),
    DEFINE_KEYCODE(DPAD_LEFT),
    DEFINE_KEYCODE(DPAD_RIGHT),
    DEFINE_KEYCODE(DPAD_CENTER),
    DEFINE_KEYCODE(VOLUME_UP),
    DEFINE_KEYCODE(VOLUME_DOWN),
    DEFINE_KEYCODE(POWER),
    DEFINE_KEYCODE(CAMERA),
    DEFINE_KEYCODE(CLEAR),
    … …

    { NULL, 0 }
};
//Keycodes.h部分代码
enum {
    AKEYCODE_UNKNOWN         = 0,
    AKEYCODE_SOFT_LEFT       = 1,
    AKEYCODE_SOFT_RIGHT      = 2,
    AKEYCODE_HOME            = 3,
    AKEYCODE_BACK            = 4,
    AKEYCODE_CALL            = 5,
    … …
    AKEYCODE_POUND           = 18,
    AKEYCODE_DPAD_UP         = 19,
    AKEYCODE_DPAD_DOWN       = 20,
    AKEYCODE_DPAD_LEFT       = 21,
    AKEYCODE_DPAD_RIGHT      = 22,
    AKEYCODE_DPAD_CENTER     = 23,
    AKEYCODE_VOLUME_UP       = 24,
    AKEYCODE_VOLUME_DOWN     = 25,
    AKEYCODE_POWER           = 26,
    AKEYCODE_CAMERA          = 27,
    AKEYCODE_CLEAR           = 28,
    … …
    // NOTE: If you add a new keycode here you must also add it to several other files.
    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
};

//Keycodes.h部分代码
enum {
    AKEYCODE_UNKNOWN         = 0,
    AKEYCODE_SOFT_LEFT       = 1,
    AKEYCODE_SOFT_RIGHT      = 2,
    AKEYCODE_HOME            = 3,
    AKEYCODE_BACK            = 4,
    AKEYCODE_CALL            = 5,
    … …
    AKEYCODE_POUND           = 18,
    AKEYCODE_DPAD_UP         = 19,
    AKEYCODE_DPAD_DOWN       = 20,
    AKEYCODE_DPAD_LEFT       = 21,
    AKEYCODE_DPAD_RIGHT      = 22,
    AKEYCODE_DPAD_CENTER     = 23,
    AKEYCODE_VOLUME_UP       = 24,
    AKEYCODE_VOLUME_DOWN     = 25,
    AKEYCODE_POWER           = 26,
    AKEYCODE_CAMERA          = 27,
    AKEYCODE_CLEAR           = 28,
    … …
    // NOTE: If you add a new keycode here you must also add it to several other files.
    //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
};

这样就将物理按键、kernel、上层之间的映射关系确定了,点击某个物理按键,上层就知道哪个按键被点击了。

如果我要将音量+/-按键变为上/下按键,只需要把对应kl文件中,104和105对应的VOLUME_UP和VOLUME_DOWN更改为DPAD_UP和DPAD_DOWN即可。整编后将系统烧到手机,重新开机就可以了。