Android6.0 按鍵kl檔案載入過程分析
在之前按鍵過程分析的幾篇部落格中,我分析過關於按鍵kl檔案的載入,但是講的不是非常詳細,這篇部落格主要把kl檔案載入過程單獨拉出來分析下。
1. 獲取InputDeviceIdentifier的name 以及 Device的建立
InputDeviceIdentifier的name 非常重要,後面尋找idc kl kcm檔案都需要這個name。
我們看下面的呼叫流程EventHub::getEvents -> EventHub::scanDevicesLocked -> EventHub::scanDirLocked -> EventHub::openDeviceLocked
我們來看EventHub::openDeviceLocked函式,先是開啟devicePath,然後利用ioctl獲取InputDeviceIdentifier的name
status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; ALOGE("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } InputDeviceIdentifier identifier; // Get device name. if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name.setTo(buffer); }
下面建立了Device,各種列印。
Device* device = new Device(fd, deviceId, String8(devicePath), identifier); ALOGW("add device %d: %s\n", deviceId, devicePath); ALOGW(" bus: %04x\n" " vendor %04x\n" " product %04x\n" " version %04x\n", identifier.bus, identifier.vendor, identifier.product, identifier.version); ALOGW(" name: \"%s\"\n", identifier.name.string()); ALOGW(" location: \"%s\"\n", identifier.location.string()); ALOGW(" unique id: \"%s\"\n", identifier.uniqueId.string()); ALOGW(" descriptor: \"%s\"\n", identifier.descriptor.string()); ALOGW(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); // Load the configuration file for the device. loadConfigurationLocked(device);
先來看下列印
EventHub: Opening device: /dev/input/event4
EventHub: Created descriptor: raw=:0000:0000:name:comip_snd_soc Headset, cooked=2efc90e2a7d3beb2de2b795a507e8489f0acd57f
EventHub: add device 1: /dev/input/event4
EventHub: bus: 0000
EventHub: vendor 0000
EventHub: product 0000
EventHub: version 0000
EventHub: name: "comip_snd_soc Headset"
EventHub: location: "ALSA"
EventHub: unique id: ""
EventHub: descriptor: "2efc90e2a7d3beb2de2b795a507e8489f0acd57f"
EventHub: driver: v1.0.1
2. 載入idc檔案
在我們的裝置中,一般沒有定義自己的idc檔案,也就找不到。一般定義idc檔案,是在這個檔案中定義kl 和kcm檔案。
我們再來分析下loadConfigurationLocked函式,呼叫getInputDeviceConfigurationFilePathByDeviceIdentifier函式,當configurationFile不為空的時候,呼叫PropertyMap::load載入idc檔案,這部分程式碼是在system/libutil下面的,當有這個idc檔案的時候,device->configuration就不為空。
void EventHub::loadConfigurationLocked(Device* device) {
device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier(
device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION);
if (device->configurationFile.isEmpty()) {//configurationFile為空
ALOGD("No input device configuration file found for device '%s'.",
device->identifier.name.string());
} else {//如果有configurationFile檔案,那我們就呼叫PropertyMap::load函式
ALOGD("input device configuration file name '%s'.",
device->configurationFile.string());
status_t status = PropertyMap::load(device->configurationFile,
&device->configuration);
if (status) {
ALOGE("Error loading input device configuration file for device '%s'. "
"Using default configuration.",
device->identifier.name.string());
}
}
}
呼叫了getInputDeviceConfigurationFilePathByDeviceIdentifier函式,其中type為0,代表是idc檔案
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {//不進入這個分支
if (deviceIdentifier.version != 0) {
// Try vendor product version.
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
}
}
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type));
if (!productPath.isEmpty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}
於是我們再來看getInputDeviceConfigurationFilePathByName函式:
String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
path.setTo(getenv("ANDROID_ROOT"));
path.append("/usr/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path.setTo(getenv("ANDROID_DATA"));
path.append("/system/devices/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Not found.
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
name.string(), type);
return String8();
}
這個函式就是尋找各種匹配的idc檔案,最後沒找到就返回一個空的String。我們來看下appendInputDeviceConfigurationFileRelativePath函式
static void appendInputDeviceConfigurationFileRelativePath(String8& path,
const String8& name, InputDeviceConfigurationFileType type) {
path.append(CONFIGURATION_FILE_DIR[type]);
for (size_t i = 0; i < name.length(); i++) {
char ch = name[i];
if (!isValidNameChar(ch)) {
ch = '_';
}
path.append(&ch, 1);
}
path.append(CONFIGURATION_FILE_EXTENSION[type]);
}
static const char* CONFIGURATION_FILE_DIR[] = {
"idc/",
"keylayout/",
"keychars/",
};
static const char* CONFIGURATION_FILE_EXTENSION[] = {
".idc",
".kl",
".kcm",
};
這個函式就是用傳進來的路徑和名字,組成一個idc檔案。然後在getInputDeviceConfigurationFilePathByName檔案中看用appendInputDeviceConfigurationFileRelativePath檔案組成的idc檔案是否有這個檔案,有那就找到了返回idc的檔案路徑,如果沒有最後返回一個空的string。我們看我們的這段log。
InputDevice: Probing for system provided input device configuration file: path='/system/usr/idc/comip_snd_soc_Headset.idc'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/idc/comip_snd_soc_Headset.idc'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=0
EventHub: No input device configuration file found for device 'comip_snd_soc Headset'.
這段log說明沒有這樣的idc檔案。
[email protected]:/system/usr/idc # ls
AVRCP.idc
qwerty.idc
qwerty2.idc
我們看我們手機上的idc目錄,都是原生的,也就是framework/base/data下面的檔案,都是原生的也就肯定找不到匹配的idc檔案。idc檔案中儲存這kl kcm檔案的名字。
下面是qwerty.idc檔案,下面是它的內容,keyboard.layout代表kl的檔名,keyboard.characterMap代表kcm的檔名。
touch.deviceType = touchScreen
touch.orientationAware = 1
keyboard.layout = qwerty
keyboard.characterMap = qwerty
keyboard.orientationAware = 1
keyboard.builtIn = 1
cursor.mode = navigation
cursor.orientationAware = 1
3. 載入kl檔案
載入kl 和 kcm檔案是在openDeviceLocked函式中呼叫loadKeyMapLocked函式完成的。
那我們繼續分析openDeviceLocked函式,關於載入kl檔案的那部分程式碼:
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}
loadKeyMapLocked函式status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
我們再來看load函式,前面我們的idc檔案沒有找到匹配的,因此第一個分支可以直接跳過,可以直接看probeKeyMap函式。
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but "
"it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
if (status == NAME_NOT_FOUND) {
ALOGE("Configuration for keyboard device '%s' requested keyboard character "
"map '%s' but it was not found.",
deviceIdenfifier.name.string(), keyLayoutName.string());
}
}
if (isComplete()) {
return OK;
}
}
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK;
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {
return OK;
}
// Give up!
ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
deviceIdenfifier.name.string());
return NAME_NOT_FOUND;
}
3.1 沒有找到匹配InputDeviceIdentifier的name的kl檔案 使用原生的Generic.kl檔案
第一種情況是沒有找到匹配InputDeviceIdentifier的name的kl檔案,這個時候我們一般就用Generic.kl檔案代替。
下面我們直接看probeKeyMap函式:
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& keyMapName) {
if (!haveKeyLayout()) {//是否有kl檔案
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {//是否有kcm檔案
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
先來看下isComplete函式,kl檔案和kcm檔案都有了才返回true,看load函式,當isComplete返回true,就直接return了,因為kl 和 kcm檔案都找到了。
inline bool isComplete() const {
return haveKeyLayout() && haveKeyCharacterMap();
}
下面我們看下載入kl檔案的過程,kcm檔案的載入過程和kl類似我們就不看了。
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
ALOGE("loadKeyLayout path '%s'.",
path.string());
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
if (status) {
return status;
}
keyLayoutFile.setTo(path);
return OK;
}
先看下getPath函式,第一個在load函式中呼叫loadKeyLayout的name是空的,所以這裡就是用了getInputDeviceConfigurationFilePathByDeviceIdentifier函式。
String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type) {
return name.isEmpty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
看下getInputDeviceConfigurationFilePathByDeviceIdentifier函式,第一部分就是各種Vendor之類的kl,我們沒有走進這個分支。
String8 getInputDeviceConfigurationFilePathByDeviceIdentifier(
const InputDeviceIdentifier& deviceIdentifier,
InputDeviceConfigurationFileType type) {
if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
if (deviceIdentifier.version != 0) {
// Try vendor product version.
String8 versionPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x_Version_%04x",
deviceIdentifier.vendor, deviceIdentifier.product,
deviceIdentifier.version),
type));
if (!versionPath.isEmpty()) {
return versionPath;
}
}
// Try vendor product.
String8 productPath(getInputDeviceConfigurationFilePathByName(
String8::format("Vendor_%04x_Product_%04x",
deviceIdentifier.vendor, deviceIdentifier.product),
type));
if (!productPath.isEmpty()) {
return productPath;
}
}
// Try device name.
return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type);
}
因此我們直接看下getInputDeviceConfigurationFilePathByName函式,和之前找idc那個函式一樣,只是這裡是用來找kl檔案了
String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
path.setTo(getenv("ANDROID_ROOT"));
path.append("/usr/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path.setTo(getenv("ANDROID_DATA"));
path.append("/system/devices/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Not found.
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
name.string(), type);
return String8();
}
之前我們的log中,這個name就是comip_snd_soc Headset,也沒有找到。
EventHub: name: "comip_snd_soc Headset"
我們看下裝置有哪些kl檔案,確實沒有comip_snd_soc Headset這個檔案。
AVRCP.kl
Generic.kl
Vendor_0079_Product_0011.kl
Vendor_045e_Product_028e.kl
Vendor_046d_Product_b501.kl
Vendor_046d_Product_c216.kl
Vendor_046d_Product_c219.kl
Vendor_046d_Product_c21d.kl
Vendor_046d_Product_c21f.kl
Vendor_046d_Product_c294.kl
Vendor_046d_Product_c299.kl
Vendor_046d_Product_c532.kl
Vendor_054c_Product_0268.kl
Vendor_0583_Product_2060.kl
Vendor_05ac_Product_0239.kl
Vendor_0b05_Product_4500.kl
Vendor_1038_Product_1412.kl
Vendor_12bd_Product_d015.kl
Vendor_1532_Product_0900.kl
Vendor_1689_Product_fd00.kl
Vendor_1689_Product_fd01.kl
Vendor_1689_Product_fe00.kl
Vendor_18d1_Product_2c40.kl
Vendor_18d1_Product_5018.kl
Vendor_1949_Product_0401.kl
Vendor_1bad_Product_f016.kl
Vendor_1bad_Product_f023.kl
Vendor_1bad_Product_f027.kl
Vendor_1bad_Product_f036.kl
Vendor_1d79_Product_0009.kl
Vendor_22b8_Product_093d.kl
Vendor_2378_Product_1008.kl
Vendor_2378_Product_100a.kl
comip-gpio-keys.kl
comip-keypad.kl
ft5x06.kl
h2w_headset.kl
qwerty.kl
sensor00fn11.kl
我們回過頭在來看load函式呼叫的第二個probeKeyMap函式,是傳入了Generic引數,
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}
我們再來看看probeKeyMap函式,還是呼叫了loadKeyLayout函式
bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier,
const String8& keyMapName) {
if (!haveKeyLayout()) {
loadKeyLayout(deviceIdentifier, keyMapName);
}
if (!haveKeyCharacterMap()) {
loadKeyCharacterMap(deviceIdentifier, keyMapName);
}
return isComplete();
}
status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier,
const String8& name) {
String8 path(getPath(deviceIdentifier, name,
INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT));
if (path.isEmpty()) {
return NAME_NOT_FOUND;
}
ALOGE("kangchen loadKeyLayout path '%s'.",
path.string());
status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
if (status) {
return status;
}
keyLayoutFile.setTo(path);
return OK;
}
同樣我們來看getPath函式,這個時候name不是空了,就呼叫getInputDeviceConfigurationFilePathByName函式
String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier,
const String8& name, InputDeviceConfigurationFileType type) {
return name.isEmpty()
? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type)
: getInputDeviceConfigurationFilePathByName(name, type);
}
getInputDeviceConfigurationFilePathByName函式,最後就在這個函式中找到了Generic.kl檔案。
String8 getInputDeviceConfigurationFilePathByName(
const String8& name, InputDeviceConfigurationFileType type) {
// Search system repository.
String8 path;
path.setTo(getenv("ANDROID_ROOT"));
path.append("/usr/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system provided input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Search user repository.
// TODO Should only look here if not in safe mode.
path.setTo(getenv("ANDROID_DATA"));
path.append("/system/devices/");
appendInputDeviceConfigurationFileRelativePath(path, name, type);
ALOGD("Probing for system user input device configuration file: path='%s'", path.string());
if (!access(path.string(), R_OK)) {
ALOGD("Found");
return path;
}
// Not found.
ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
name.string(), type);
return String8();
}
找到kl檔案後,我們會對這個檔案在load函式中進行解析,這個我們就不分析了,就是把掃描碼轉換成按鍵碼之類。
status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) {
outMap->clear();
Tokenizer* tokenizer;
status_t status = Tokenizer::open(filename, &tokenizer);
if (status) {
ALOGE("Error %d opening key layout map file %s.", status, filename.string());
} else {
sp<KeyLayoutMap> map = new KeyLayoutMap();
if (!map.get()) {
ALOGE("Error allocating key layout map.");
status = NO_MEMORY;
} else {
#if DEBUG_PARSER_PERFORMANCE
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
#endif
Parser parser(map.get(), tokenizer);
status = parser.parse();
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (!status) {
*outMap = map;
}
}
delete tokenizer;
}
return status;
}
下面的數字代表掃描碼,而旁邊的程式碼鍵值碼。
key 113 VOLUME_MUTE
key 114 VOLUME_DOWN
key 115 VOLUME_UP
key 116 POWER
這樣就完成了kl檔案的載入解析。我們看下這段的logInputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/comip_snd_soc_Headset.kl'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keylayout/comip_snd_soc_Headset.kl'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=1
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/comip_snd_soc_Headset.kcm'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keychars/comip_snd_soc_Headset.kcm'
InputDevice: Probe failed to find input device configuration file: name='comip_snd_soc Headset', type=2
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/Generic.kl'
InputDevice: Found
Keyboard: loadKeyLayout path '/system/usr/keylayout/Generic.kl'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/Generic.kcm'
InputDevice: Found
EventHub: Unable to disable kernel key repeat for /dev/input/event4: Function not implemented
EventHub: New device: id=1, fd=70, path='/dev/input/event4', name='comip_snd_soc Headset', classes=0x81, configuration='', keyLayout='/system/usr/keylayout/Generic.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
上面的例子是載入了Generic.kl檔案,是因為在system/usr/keylayout下面沒有找到合適的。
3.2 找到匹配InputDeviceIdentifier的name的kl檔案
下面我們的例子是找到合適的kl檔案的,我們來看下log,注意name是ft5x06
EventHub: Opening device: /dev/input/event3
EventHub: Created descriptor: raw=:0000:0000:name:ft5x06, cooked=f2706364e2849110ed562db0c53423b5027a6cc5
EventHub: add device 2: /dev/input/event3
EventHub: bus: 0000
EventHub: vendor 0000
EventHub: product 0000
EventHub: version 0000
EventHub: name: "ft5x06"
EventHub: location: ""
EventHub: unique id: ""
EventHub: descriptor: "f2706364e2849110ed562db0c53423b5027a6cc5"
EventHub: driver: v1.0.1
InputDevice: Probing for system provided input device configuration file: path='/system/usr/idc/ft5x06.idc'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/idc/ft5x06.idc'
InputDevice: Probe failed to find input device configuration file: name='ft5x06', type=0
EventHub: No input device configuration file found for device 'ft5x06'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keylayout/ft5x06.kl'
InputDevice: Found
Keyboard: loadKeyLayout path '/system/usr/keylayout/ft5x06.kl'.
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/ft5x06.kcm'
InputDevice: Probing for system user input device configuration file: path='/data/system/devices/keychars/ft5x06.kcm'
InputDevice: Probe failed to find input device configuration file: name='ft5x06', type=2
InputDevice: Probing for system provided input device configuration file: path='/system/usr/keychars/Generic.kcm'
InputDevice: Found
EventHub: Unable to disable kernel key repeat for /dev/input/event3: Function not implemented
EventHub: New device: id=2, fd=71, path='/dev/input/event3', name='ft5x06', classes=0x15, configuration='', keyLayout='/system/usr/keylayout/ft5x06.kl', keyCharacterMap='/system/usr/keychars/Generic.kcm', builtinKeyboard=false, wakeMechanism=EPOLLWAKEUP, usingClockIoctl=true
我們再來看裝置中的kl檔案,有ft5x06.kl檔案,這樣就找到了匹配的kl檔案,而不用原生的Generic.kl了。
AVRCP.kl
Generic.kl
Vendor_0079_Product_0011.kl
Vendor_045e_Product_028e.kl
Vendor_046d_Product_b501.kl
Vendor_046d_Product_c216.kl
Vendor_046d_Product_c219.kl
Vendor_046d_Product_c21d.kl
Vendor_046d_Product_c21f.kl
Vendor_046d_Product_c294.kl
Vendor_046d_Product_c299.kl
Vendor_046d_Product_c532.kl
Vendor_054c_Product_0268.kl
Vendor_0583_Product_2060.kl
Vendor_05ac_Product_0239.kl
Vendor_0b05_Product_4500.kl
Vendor_1038_Product_1412.kl
Vendor_12bd_Product_d015.kl
Vendor_1532_Product_0900.kl
Vendor_1689_Product_fd00.kl
Vendor_1689_Product_fd01.kl
Vendor_1689_Product_fe00.kl
Vendor_18d1_Product_2c40.kl
Vendor_18d1_Product_5018.kl
Vendor_1949_Product_0401.kl
Vendor_1bad_Product_f016.kl
Vendor_1bad_Product_f023.kl
Vendor_1bad_Product_f027.kl
Vendor_1bad_Product_f036.kl
Vendor_1d79_Product_0009.kl
Vendor_22b8_Product_093d.kl
Vendor_2378_Product_1008.kl
Vendor_2378_Product_100a.kl
comip-gpio-keys.kl
comip-keypad.kl
ft5x06.kl
h2w_headset.kl
qwerty.kl
sensor00fn11.kl
4. 使用kl檔案,將掃描碼,轉換成按鍵碼:
之前我們在按鍵流程(一),已經講解了按鍵最後到各個InputMapper中的process函式中處理,下面我們看這個函式,我們呼叫EventHub的mapKey來將掃描碼轉換成按鍵碼。
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
int32_t keyCode;
uint32_t flags;
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {//掃描碼對應成按鍵碼
keyCode = AKEYCODE_UNKNOWN;
flags = 0;
}
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
最後在processKey函式中,將傳送到InputDispatch中做後續處理。這個我們在之前的部落格中也分析過了。
我們再來看看mapKey函式,先處理的kcm,再處理kl的。
status_t EventHub::mapKey(int32_t deviceId,
int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
status_t status = NAME_NOT_FOUND;
if (device) {
// Check the key character map first.
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm != NULL) {
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
*outFlags = 0;
status = NO_ERROR;
}
}
// Check the key layout next.
if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
if (!device->keyMap.keyLayoutMap->mapKey(
scanCode, usageCode, outKeycode, outFlags)) {
status = NO_ERROR;
}
}
if (status == NO_ERROR) {
if (kcm != NULL) {
kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
} else {
*outMetaState = metaState;
}
}
}
if (status != NO_ERROR) {
*outKeycode = 0;
*outFlags = 0;
*outMetaState = metaState;
}
return status;
}
至於詳細分析kcm的mapKey和kl的mapKey放在以後分析了
相關推薦
Android6.0 按鍵kl檔案載入過程分析
在之前按鍵過程分析的幾篇部落格中,我分析過關於按鍵kl檔案的載入,但是講的不是非常詳細,這篇部落格主要把kl檔案載入過程單獨拉出來分析下。 1. 獲取InputDeviceIdentifier的name 以及 Device的建立 InputDeviceIdentifier
Android執行時ART載入OAT檔案的過程分析
在前面一文中,我們介紹了Android執行時ART,它的核心是OAT檔案。OAT檔案是一種Android私有ELF檔案格式,它不僅包含有從DEX檔案翻譯而來的本地機器指令,還包含有原來的DEX檔案內容。這使得我們無需重新編譯原有的APK就可以讓它正常地在ART裡
spring boot environment載入過程分析
environment是在printBanner之前就初始化好了, 更在context建立之前, 已經載入application-xxxx.properties, System.properties, System.environment ... 也可以自己監聽應用
asp.netcore 深入瞭解配置檔案載入過程
前言 配置檔案中程式執行中,擔當著不可或缺的角色;通常情況下,使用 visual studio 進行建立專案過程中,專案配置檔案會自動生成在專案根目錄下,如 appsettings.json,或者是被大家廣泛使用的 appsettings.{env.EnvironmentName}.json;配置檔
hadoop 1.0.3 fsimage 檔案寫原始碼分析
2013-01-08 周海漢 2013.1.8 上一篇文章《hadoop 1.0.4 fsimage 檔案格式分析》描述了hadoop1.04的fsimage的格式。 本篇看看hadoop 1.0.3原始碼是如何實現的。fsim
SystemUI之快捷設定區域載入過程分析
佈局構成 詳細說明,快捷設定區域的佈局是由 StatusBar.java的 makeStatusBarView ()統一載入,通過方法 inflateStatusBarWindow 方法載入佈局 s
Android6.0 Reset恢復出廠設定流程分析
點選Settings應用中的恢復出廠設定按鈕後流程分析:先使用grep命令搜尋"恢復出廠設定"字串,找到相應的佈局檔案: packages/apps/Settings/res/xml/privacy_settings.xml <PreferenceScree
[Android6.0][RK3399] Type-C 驅動流程分析
基本概念 USB 控制器 OHCI(Open Host Controller Interface) 是支援USB1.1的標準,但它不僅僅是針對USB,還支援其他的一些介面,比如它還支援Apple的火線(Firewire,IEEE 1394
Android5.0框架層簡訊接收過程分析
本文分析使用的是android5.0的原始碼,涉及的相關檔案: frameworks\opt\telephony\src\java\com\android\internal\telephony\RIL.java frameworks\base\core\java\com\a
android6.0按鍵處理淺析
處理流程及示意圖: 1,硬體配置: kernel-3.18\arch\arm\boot\dts\projectxxx.dts &keypad { mediatek,kpd-key-debounce= <1024>;
web.xml被檔案載入過程,各節點載入順序總結
web.xml載入過程(步驟):1.啟動WEB專案的時候,容器(如:Tomcat)會去讀它的配置檔案web.xml.讀兩個節點: <listener></listener> 和 <context-param></context-
C/C++程式從編譯到最終生成可執行檔案的過程分析
*******************************************************篇一*******************************************************************************************
全志A10/A20 Bootloader載入過程分析
【轉】A10/A20 Bootloader載入過程分析 轉自:http://blog.csdn.net/allen6268198/article/details/12905425 A10/A20 Bootloader載入過程分析 注:由於全志A10和A20在載入Bootlo
[RK3288][Android6.0] WiFi之Framework連線過程小結
Platform: Rockchip OS: Android 6.0Kernel: 3.10.92 onPreferenceTreeClick -> WifiSettings.java preference.getAccessPoint //獲取當前選擇的ap
Activity中佈局資源layoutResId在setContentView載入過程分析
前言 記得剛開始學習安卓那會,感覺安卓真的很簡單,用xml寫一個佈局,然後再寫一個activity,接著呼叫一下在onCreate中呼叫下setContentView(resId)一個頁面就可以看到了,現在回想也才知道Android的牛逼,它降低了開發者的門檻
Fragment載入過程分析。
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { // Fragments that are not currently
A10/A20 Bootloader載入過程分析
上面羅嗦了這麼多,其實就是為了將uboot和kernel燒寫到TF卡上並能夠啟動,OK,讓我們先從分割槽開始: A20 晶片上電啟動的時候,會讀取SD卡最前面的 1M 內容,從而得到 bootloader,所以我們需要把 u-boot 寫到SD卡的前1M區間。 其中詳細的SD卡布局如下: 起始 大小
全志A10 Bootload載入過程分析
A10的啟動過程大概可分為5步:BootRom,SPL,Uboot,Kernel,RootFileSystem。本文只關注映象的載入過程,分析RootRom->SPL->Uboot的啟動流程。 系統上電後,ARM處理器在復位時從地址0x000000開始執行
Android6.0自帶檔案管理器無法開啟apk
Android從6.0開始在設定中自帶了一個檔案瀏覽器,在6.0之前系統是不自帶的,但是這個檔案管理器不能開啟apk檔案,不能安裝app。那是因為程式碼裡面沒有對APK檔案做識別處理,下面這個補丁可以幫你搞定. diff --git a/packages/Documents
android6.0預設Home(Launcher3)的啟動分析
Launcher是預設的桌面應用,在系統啟動後開始啟動Launcher,進而才載入桌面資料。那麼如何實現開機進入預設Launcher,比如把自己寫的應用設定成開機預設啟動的桌面呢?帶著這個問題來分析Launcher是如何被選中併成為預設桌面應用而啟動的。 Sy