参考原文:http://blog.csdn.net/skdev/archive/2010/03/08/5355542.aspx
1、WindowManager服务
文件:frameworks/base/services/java/com/android/server/WindowManagerService.java
WindowManagerService的构造函数中会创建内部类KeyQ对象。
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods) {
if (MEASURE_LATENCY) {
lt = new LatencyTimer(100, 1000);
}
...
mQueue = new KeyQ();
...
}
2、KeyInputQueue类
文件:frameworks/base/services/java/com/android/server/KeyInputQueue.java
KeyQ继承于KeyInputQueue类,在KeyInputQueue类的构造函数中会启动一个线程mThread,在这个线程里不断读取输入事件,然后对这个事件进行处理:
KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
......
mThread.start();
}
mThread的定义如下:
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
......
RawInputEvent ev = new RawInputEvent();
while (true) {
try {
...
// block, doesn't release the monitor
readEvent(ev);
...
}
...
synchronized (mFirst) {
...
final int classes = di.classes;
final int type = ev.type;
final int scancode = ev.scancode;
...
// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
...
}
}
}
}
mThread不断调用readEvent,将输入事件读取到ev类,即RawInputEvent的变量里,然后会判断输入事件的类型。比如如果如下条件成立,
type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST || scancode > RawInputEvent.BTN_LAST)
则会判断成键盘事件。
判断会相应事件后,会调用addLocked函数把相应的事件加入到事件队列中去:
private void addLocked(InputDevice device, long whenNano, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;
QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
QueuedEvent p = mLast.prev;
while (p != mFirst && ev.whenNano < p.whenNano) {
p = p.prev;
}
ev.next = p.next;
ev.prev = p;
p.next = ev;
ev.next.prev = ev;
ev.inQueue = true;
if (poke) {
long time;
if (MEASURE_LATENCY) {
time = System.nanoTime();
}
mFirst.notify();
mWakeLock.acquire();
if (MEASURE_LATENCY) {
lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
}
}
}
这个事件队列的类型是QueuedEvent
3、readEvent的JNI实现:android_server_KeyInputQueue_readEvent
文件:frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp
static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
{
gLock.lock();
sp<EventHub> hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen,
(jlong)(nanoseconds_to_milliseconds(when)));
return res;
}
此函数将从eventhub读到的变量存到android/view/RawInputEvent类对应的变量里。为什么呢?这里要简单说明一下。
3.1 register_android_server_KeyInputQueue的定义
文件:frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp
int register_android_server_KeyInputQueue(JNIEnv* env)
{
...
jclass inputEvent = env->FindClass("android/view/RawInputEvent");
LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent");
gInputOffsets.mDeviceId
= env->GetFieldID(inputEvent, "deviceId", "I");
LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId");
gInputOffsets.mType
= env->GetFieldID(inputEvent, "type", "I");
LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type");
gInputOffsets.mScancode
= env->GetFieldID(inputEvent, "scancode", "I");
LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode");
gInputOffsets.mKeycode
= env->GetFieldID(inputEvent, "keycode", "I");
LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode");
gInputOffsets.mFlags
= env->GetFieldID(inputEvent, "flags", "I");
LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags");
gInputOffsets.mValue
= env->GetFieldID(inputEvent, "value", "I");
LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value");
gInputOffsets.mWhen
= env->GetFieldID(inputEvent, "when", "J");
LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when");
return res;
}
此函数获取android/view/RawInputEvent类的变量。
3.2 register_android_server_KeyInputQueue的被调用
文件:frameworks/base/services/jni/onload.cpp
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("GetEnv failed!");
return result;
}
LOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_KeyInputQueue(env);
register_android_server_HardwareService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
register_android_server_SensorService(env);
register_android_server_FallbackCheckinService(env);
register_android_server_SystemServer(env);
return JNI_VERSION_1_4;
}
当Android的VM(Virtual Machine)执行到System.loadLibrary()函数时,首先会去执行C组件里的JNI_OnLoad()函数。
从上可以看出,JNI_OnLoad函数还会调用其他相应的服务注册函数。
从android_server_KeyInputQueue_readEvent的定义可以看出,它会去调用hub->getEvent函数,hub的类型是EventHub,下面我们来看看EventHub类中的
getEvent函数。
4、EventHub库
文件:frameworks/base/libs/ui/EventHub.cpp
4.1 读取输入设备状态
static const char *device_path = "/dev/input";
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
...
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
mOpened = true;
}
...
while(1) {
...
pollres = poll(mFDs, mFDCount, -1);
...
// mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
mDevices[i]->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
*outDeviceId = mDevices[i]->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = iev.type;
*outScancode = iev.code;
if (iev.type == EV_KEY) {
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d/n",
iev.code, *outKeycode, *outFlags, err);
if (err != 0) {
*outKeycode = 0;
*outFlags = 0;
}
} else {
*outKeycode = iev.code;
}
*outValue = iev.value;
*outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
return true;
} else {
if (res<0) {
LOGW("could not get event (errno=%d)", errno);
} else {
LOGE("could not get event (wrong size: %d)", res);
}
continue;
}
}
}
}
// read_notify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
read_notify(mFDs[0].fd);
}
}
}
openPlatformInput() 打开/dev/input/ 目录下的所有输入设备文件。
打开设备后,不断轮循所有设备,直到读取到有POLLIN事件产生的设备的状态。
4.2 openPlatformInput函数
bool EventHub::openPlatformInput(void)
{
...
res = scan_dir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s/n", device_path);
//open_device("/dev/input/event0");
}
return true;
}
openPlatformInput函数会调用scan_dir函数:
int EventHub::scan_dir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '/0' ||
(de->d_name[1] == '.' && de->d_name[2] == '/0')))
continue;
strcpy(filename, de->d_name);
open_device(devname);
}
closedir(dir);
return 0;
}
scan_dir会递归地调用open_device函数,打开/dev/input/ 目录下的所有输入设备文件。
4.3 导入键盘配置文件
int EventHub::open_device(const char *deviceName)
{
...
// find the .kl file we need for this device
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;
}
device->layoutMap->load(keylayoutFilename);
...
char propName[100];
sprintf(propName, "hw.keyboards.%u.devname", publicID);
property_set(propName, name);
...
}
由以上代码可知,会优先加载/system/usr/keylayout/输入设备名称.kl
如: /sys/class/input/input1/name = keypad,则会加载/system/usr/keylayout/keypad.kl,如果该文件不存在,则加载默认文件/system/usr/keylayout/qwerty.kl,该文件的原型在:sdk/emulator/keymaps/qwerty.kl ,eclair以前的版本都是放在development/ emulator/keymaps/qwerty.kl
4.4 按键映射
如4.3节中open_device的代码,先加载配置文件:
device->layoutMap->load(keylayoutFilename);
如4.1节中getEvent的代码,再将读取到的按键码进行转换:
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
文件:frameworks/base/libs/ui/KeyLayoutMap.cpp
status_t
KeyLayoutMap::load(const char* filename)
{
int fd = open(filename, O_RDONLY);
...
while (true) {
String8 token = next_token(&p, &line);
if (*p == '/0') {
break;
}
switch (state)
{
case BEGIN:
if (token == "key") {
state = SCANCODE;
} else {
LOGE("%s:%d: expected key, got '%s'/n", filename, line,
token.string());
err = BAD_VALUE;
goto done;
}
break;
case SCANCODE:
scancode = strtol(token.string(), &end, 0);
if (*end != '/0') {
LOGE("%s:%d: expected scancode (a number), got '%s'/n",
filename, line, token.string());
goto done;
}
//LOGI("%s:%d: got scancode %d/n", filename, line, scancode );
state = KEYCODE;
break;
case KEYCODE:
keycode = token_to_value(token.string(), KEYCODES);
//LOGI("%s:%d: got keycode %d for %s/n", filename, line, keycode, token.string() );
if (keycode == 0) {
LOGE("%s:%d: expected keycode, got '%s'/n",
filename, line, token.string());
goto done;
}
state = FLAG;
break;
case FLAG:
if (token == "key") {
if (scancode != -1) {
//LOGI("got key decl scancode=%d keycode=%d"
// " flags=0x%08x/n", scancode, keycode, flags);
Key k = { keycode, flags };
m_keys.add(scancode, k);
state = SCANCODE;
scancode = -1;
keycode = -1;
flags = 0;
break;
}
}
tmp = token_to_value(token.string(), FLAGS);
//LOGI("%s:%d: got flags %x for %s/n", filename, line, tmp, token.string() );
if (tmp == 0) {
LOGE("%s:%d: expected flag, got '%s'/n",
filename, line, token.string());
goto done;
}
flags |= tmp;
break;
}
}
...
}
由以上代码可知,是以如下方式对配置文件进行解析的:
BEGIN: 如果第一个关键字是 key ,则转入 SCANCODE ,否则退出。
SCANCODE: 将第二个关键字转为数字,即扫描码 scancode ,转向 KEYCODE 。
KEYCODE: 将第三个关键字与 KEYCODES 列表配对,找出关键码 keycode ,转向 FLAG 。
FLAG: 如果第四个关键字是 key ,则保存刚扫描的键码,然后转向 SCANCODE ,否则将该关键字与 FLAGS 列表配对,找出 flags 值,然后转向 BEGIN 。
注: KEYCODES 列表和 FLAGS 列表的定义在:
frameworks/base/include/ui/KeycodeLabels.h
status_t
KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const
{
if (m_status != NO_ERROR) {
return m_status;
}
ssize_t index = m_keys.indexOfKey(scancode);
if (index < 0) {
//LOGW("couldn't map scancode=%d/n", scancode);
return NAME_NOT_FOUND;
}
const Key& k = m_keys.valueAt(index);
*keycode = k.keycode;
*flags = k.flags;
//LOGD("mapped scancode=%d to keycode=%d flags=0x%08x/n", scancode,
// keycode, flags);
return NO_ERROR;
}
Map 的功能是根据 scancode ,找到对应的 keycode.
qwerty.kl 的部分配置:
# scancode keycode flags
key 399 GRAVE
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 158 BACK WAKE_DROPPED
key 230 SOFT_RIGHT WAKE
key 60 SOFT_RIGHT WAKE
5、流程总结
当有按键响应时,内核传给 ANDROID 的是 scancode , ANDROID 将 scancode 经配置表 ( 如 qwerty.kl) 找到 keycode 标识符,然后由内部表 KEYCODES 列表找到 keycode 的数字值。