android 能夠將不同的低層 scancode 轉化成上層使用的統一的 keycode (以下分析爲 android 2.2 froyo 的)。下面說的幾個相關的源代碼文件都在 framework/base/libs/ui 下。
EventHub? .cpp
先看看下面這段代碼:// 在 open_device 函數裏 if ((device->classes&CLASS_KEYBOARD) != 0) { char devname[PROPERTY_VALUE_MAX]; char keylayoutFilename[300]; const char* root = getenv("ANDROID_ROOT"); property_get("persist.sys.keylayout", devname, "qwerty"); snprintf(keylayoutFilename, sizeof(keylayoutFilename), "%s/usr/keylayout/%s.kl", root, devname); bool defaultKeymap = access(keylayoutFilename, R_OK); if (defaultKeymap) { strcpy(devname, "qwerty"); snprintf(keylayoutFilename, sizeof(keylayoutFilename), "%s/usr/keylayout/%s.kl", root, devname); } LOGI("keylayout = %s, Filename = %s", devname, keylayoutFilename); device->layoutMap->load(keylayoutFilename);
kl 文件
這個文件就是 android 的按鍵映射文件,結構如下:
這裏配合看下下面的代碼,在 KeyLayoutMap? .cpp 裏:
status_t KeyLayoutMap::load(const char* filename) { int fd = open(filename, O_RDONLY); if (fd < 0) { LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); m_status = errno; return errno; } off_t len = lseek(fd, 0, SEEK_END); off_t errlen = lseek(fd, 0, SEEK_SET); if (len < 0 || errlen < 0) { close(fd); LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); m_status = errno; return errno; } char* buf = (char*)malloc(len+1); if (read(fd, buf, len) != len) { LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); } errno = 0; buf[len] = '\0'; int32_t scancode = -1; int32_t keycode = -1; uint32_t flags = 0; uint32_t tmp; char* end; status_t err = NO_ERROR; int line = 1; char const* p = buf; enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; 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; } } 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; }
可以看得出,android 會解析 kl 文件裏的項目,然後把解析後的結果保存到一個 Vector 結構裏(android 自己寫一個 vector,不是 c++ std 的)。 kl 文件的每一項分爲4段:
- BEGIN
- SCANCODE
- KEYCODE
- FLAG
KeycodeLabels? .h
上面說了 KEYCODE 那裏很多是保存字符串的,那麼轉化的地方就在這個文件裏(這個文件在 framework/base/include/ui 下):struct KeycodeLabel { const char *literal; int value; }; // 這裏不貼全了,都在這 static const KeycodeLabel KEYCODES[] = { { "SOFT_LEFT", 1 }, { "SOFT_RIGHT", 2 }, { "HOME", 3 }, { "BACK", 4 }, { "CALL", 5 }, { "ENDCALL", 6 }, { "0", 7 }, { "1", 8 }, { "2", 9 }, { "3", 10 }, { "4", 11 }, { "5", 12 }, { "6", 13 }, { "7", 14 }, { "8", 15 }, { "9", 16 }, { "STAR", 17 }, { "POUND", 18 }, { "DPAD_UP", 19 }, ... ...
前面說的那個 load 代碼裏 KEYCODE 那一段的 keycode = token_to_value(token.string(), KEYCODES); 中的 token_to_value 就是通過上面這個數組來得到對應的數值的。這個數值就是 android sdk 裏寫的給上層應用程序使用的按鍵值了。所以一般來說,我們修改 kl 文件就可以改變按鍵映射了。