Eventhub是在Native 的inputmanager创建的时候被创建的,inputreadthread就通过eventhub的getevent方法来源源不断的获取driver上报的input事件。
bool EventHub::getEvent(RawEvent* outEvent)
{
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR :UNKNOWN_ERROR;
mOpened = true;
mNeedToSendFinishedDeviceScan = true;
}
//省略
}
如果是第一次进入getevent,则会去调用openplatforminput来获取input设备,加载对应的kl,将device加入eventhub的特定的结构体中。
bool EventHub::openPlatformInput(void)
{
//省略
res = scanDir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s\n", device_path);
}
return true;
}
直接调用scandir来处理/dev/input/文件夹
int EventHub::scanDir(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);
openDevice(devname);
}
closedir(dir);
return 0;
}
通过readdir来得到子文件的名字,拼成完整的设备路径后调用opendevice来打开他,如/dev/input/event0 (可以在adb shell进入手机后cd到/dev/input下ls下,就可以看到了)
int EventHub::openDevice(const char*deviceName) {
//省略,主要是将新发现的设备增加到mDevicesById和mFDs数组中。
//接下来是对设备类型的判断,我们以键盘为例:
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >=0) {
//LOGI("MAP\n");
//for (int i = 0; i < sizeof(key_bitmask); i++) {
// LOGI("%d:0x%02x\n", i, key_bitmask[i]);
//}
// See if this is a keyboard. Ignoreeverything in the button range except for
// gamepads which are also considered keyboards.
if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
||containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
sizeof_bit_array(BTN_DIGI))
||containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1))) {
device->classes |=INPUT_DEVICE_CLASS_KEYBOARD;
device->keyBitmask = newuint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask,key_bitmask, sizeof(key_bitmask));
} else {
delete device;
LOGE("out of memory allocating keybitmask");
return -1;
}
}
}
//具体的ioctl还没搞明白,从字面意思上看,就是判断当前设备是否是键盘,如果是则将其类型或上INPUT_DEVICE_CLASS_KEYBOARD
//其他的设备的处理也很类似,接下来是加载kl:
if((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
//如果是键盘
char tmpfn[sizeof(name)];
char keylayoutFilename[300];
// a more descriptive name
device->name = name;
// replace all the spaces with underscores
strcpy(tmpfn, name);
for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ''))
*p = '_';
// find the .kl file we need for this device
const char* root = getenv("ANDROID_ROOT");
//此处的root即是/system/
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;
}
status_t status = device->layoutMap->load(keylayoutFilename);
if (status) {
LOGE("Error %d loading key layout.", status);
}
device->next = mOpeningDevices;
mOpeningDevices = device;
mDevices[mFDCount] = device
mFDCount++;
//首先尝试去查找是否存在特定的keylayoutfile,如我们的键盘对应的kl应该是:
/system/usr/keylayout/sprd-keypad.kl
如果kl不存在,则尝试去打开默认的kl,即qwerty.kl,然后加载对应的kl。
//如果加载成功,则通知大家设备名(将hw.keyboards.0.devname赋值为对应的设备名,如sprd-keypad),并将其声明为mOpeningDevices到这里,一个input设备就打开成功可以为eventhub所用了。
}
接下来我们看下keylayoutMap的Load函数来了解下kl文件的解析。
Kl文件也比较简单,截取一部分:
Key 116 POWER WAKE
Key是起始标志, 116是硬件扫描码,POWER是对应的KEYCODE,WAKE是按键对应的FLAG。
KeyLayoutMap::load(const char* filename){
//省略无关。
enum { BEGIN,SCANCODE, KEYCODE, FLAG } state = BEGIN;
//state初始为BEGIN
while (true) {
String8 token = next_token(&p, &line);
if (*p == '\0') {
break;
}
switch (state)
{
case BEGIN:
if (token == "key") {
state = SCANCODE;
//如果读取到key,状态置为SCANCODE
} else {
LOGE("%s:%d: expectedkey, 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: expectedscancode (a number), got '%s'\n",
filename, line,token.string());
goto done;
}
//LOGI("%s:%d: gotscancode %d\n", filename, line, scancode );
//
state = KEYCODE;
//读取扫描码成功后继续读取keycode
break;
case KEYCODE:
keycode =token_to_value(token.string(), KEYCODES);
//通过在KEYCODES中查找得到对应的按键码,如POWER:
staticconst KeycodeLabel KEYCODES[] = {
{"SOFT_LEFT", 1 },
{"SOFT_RIGHT", 2 },
{"HOME", 3 },
{"BACK", 4 },
{ "POWER", 26 },
}对应的是KEYCODE_POWER
此处的keycode就是上层的使用的keycode。通过kl,就将硬件扫描码转换成了keycode。
//LOGI("%s:%d: got keycode%d for %s\n", filename, line, keycode, token.string() );
if (keycode == 0) {
LOGE("%s:%d: expectedkeycode, got '%s'\n",
filename, line,token.string());
goto done;
}
state = FLAG;
break;
case FLAG:
if (token == "key") {
if (scancode != -1) {
//LOGI("got keydecl 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);
//类似的,查FLAGS数组,找到对应的FLAGS:
static const KeycodeLabel FLAGS[] = {
{"WAKE", 0x00000001 },
WAKE,就是0x00000001,对应的是 POLICY_FLAG_WAKE =0x00000001,在input.h
//LOGI("%s:%d: got flags%x for %s\n", filename, line, tmp, token.string() );
if (tmp == 0) {
LOGE("%s:%d: expectedflag, got '%s'\n",
filename, line,token.string());
goto done;
}
flags |= tmp;
break;
}
}
if (state == FLAG && 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);
}
done:
free(buf);
close(fd);
m_status = err;
return err;
}
这样,对应的keycode scancode,flag,都保存在了m_keys中。
接下来继续看getevent:这个神一样的函数
boolEventHub::getEvent(RawEvent* outEvent)
{
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR: UNKNOWN_ERROR;
mOpened = true;
mNeedToSendFinishedDeviceScan = true;
//如果发现了新设备,mNeedToSendFinishedDeviceScan被置为true,在下面会用到
}
for (;;) {
// Report any devices that had lastbeen added/removed.
if (mClosingDevices != NULL) {
//在closeDevice函数中被赋值,如果有设备需要关闭,则会走进来,返回一个DEVICE_REMOVED事件。
device_t* device = mClosingDevices;
LOGV("Reporting device closed:id=0x%x, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
if (device->id ==mFirstKeyboardId) {
outEvent->deviceId = 0;
} else {
outEvent->deviceId =device->id;
}
outEvent->type = DEVICE_REMOVED;
outEvent->when =systemTime(SYSTEM_TIME_MONOTONIC);
delete device;
mNeedToSendFinishedDeviceScan =true;
return true;
}
if (mOpeningDevices != NULL) {
// 在上文中的opendevice中的末尾,会将其置为对应的device,也就是说如果发现了新设备,会返回一个DEVICE_ADDED的事件。在inputreader中会处理
device_t* device = mOpeningDevices;
LOGV("Reporting device opened:id=0x%x, name=%s\n",
device->id,device->path.string());
mOpeningDevices = device->next;
if (device->id ==mFirstKeyboardId) {
outEvent->deviceId = 0;
} else {
outEvent->deviceId =device->id;
}
outEvent->type = DEVICE_ADDED;
outEvent->when =systemTime(SYSTEM_TIME_MONOTONIC);
mNeedToSendFinishedDeviceScan =true;
return true;
}
if (mNeedToSendFinishedDeviceScan) {
//在ADD完成设备后,会发出一个FINISHD_DEVICE_SCAN,标志着所有的input设备都已经SCAN完成。
mNeedToSendFinishedDeviceScan =false;
outEvent->type =FINISHED_DEVICE_SCAN;
outEvent->when =systemTime(SYSTEM_TIME_MONOTONIC);
return true;
}
//接下来就是对按键的捕获处理
// Grab the next input event.
for (;;) {
// Consume buffered input events,if any.
if (mInputBufferIndex <mInputBufferCount) {
//如果当前存在需要处理的按键事件,则通过map返回对应的outevent
const struct input_event&iev = mInputBufferData[mInputBufferIndex++];
const device_t* device =mDevices[mInputDeviceIndex];
LOGV("%s got: t0=%d,t1=%d, type=%d, code=%d, v=%d", device->path.string(),
(int) iev.time.tv_sec,(int) iev.time.tv_usec, iev.type, iev.code, iev.value);
if (device->id ==mFirstKeyboardId) {
outEvent->deviceId = 0;
//keypad的设备id为0
} else {
outEvent->deviceId =device->id;
}
outEvent->type = iev.type;
outEvent->scanCode =iev.code;
if (iev.type == EV_KEY) {
status_t err = device->layoutMap->map(iev.code,
&outEvent->keyCode, & outEvent->flags);
//将扫描码转换成按键码,并给出按键的flag位
通过对应device的layoutMap中的m_keys中存储的信息(上文中的load中可以看到),返回对应的按键码,FLAG。
LOGV("iev.code=%dkeyCode=%d flags=0x%08x err=%d\n",
iev.code, outEvent->keyCode,outEvent->flags, err);
if (err != 0) {
outEvent->keyCode =AKEYCODE_UNKNOWN;
outEvent->flags = 0;
}
} else {
outEvent->keyCode =iev.code;
}
outEvent->value = iev.value;
// Use an event timestamp inthe same timebase as
// java.lang.System.nanoTime()and android.os.SystemClock.uptimeMillis()
// as expected by the rest of the system.
outEvent->when =systemTime(SYSTEM_TIME_MONOTONIC);
return true;
}
// 如果读取完了所有存在buffer里的按键事件,或者因为设备刚初始化,不存在等待读取的按键事件,则去读。
mInputDeviceIndex += 1;
if (mInputDeviceIndex >= mFDCount) {
break;
}
const struct pollfd& pfd =mFDs[mInputDeviceIndex];
if (pfd.revents & POLLIN) {
int32_t readSize = read(pfd.fd, mInputBufferData,
sizeof(structinput_event) * INPUT_BUFFER_SIZE);
if (readSize < 0) {
if (errno != EAGAIN&& errno != EINTR) {
LOGW("could notget event (errno=%d)", errno);
}
} else if ((readSize %sizeof(struct input_event)) != 0) {
LOGE("could not getevent (wrong size: %d)", readSize);
} else {
mInputBufferCount= readSize / sizeof(struct input_event);
//将input事件存到buffer中,并计算出count,且将index赋值为0
mInputBufferIndex = 0;
}
}
这样,inputreadthread不停的调用getevent,不停的可以获取到当前的input事件,并上报处理。
Eventhub的部分就看到这里,下一篇看kernel和上层是怎么对应上的。