android系統的輸入事件來源在linux內核提供的/dev/input
的設備節點下, 當該設備下及誒點有數據刻度時,將數據獨處並進行一系列的翻譯和加工,而後在全部的窗口中尋找合適的接受者,並派發給它;linux
輸入系統整體流程以下(引之深刻理解android卷3 ): android
爲了接下來說解原理方便, 在這裏模擬一個輸入設備, 方法-寫一個驅動;ubuntu
/* 參考drivers\input\keyboard\gpio_keys.c */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/input.h> static struct input_dev *input_emulator_dev; static int input_emulator_init(void) { int i; int ret; /* 1. 分配一個input_dev結構體 */ input_emulator_dev = input_allocate_device(); /* 2. 設置 */ /* 2.1 能產生哪類事件 */ set_bit(EV_KEY, input_emulator_dev->evbit); set_bit(EV_REP, input_emulator_dev->evbit); /* 2.2 能產生全部的按鍵 */ for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) input_emulator_dev->keybit[i] = ~0UL; /* 2.3 爲android構造一些設備信息 */ input_emulator_dev->name = "smart remote"; input_emulator_dev->id.bustype = 1; input_emulator_dev->id.vendor = 0x0001; input_emulator_dev->id.product = 0x0010; input_emulator_dev->id.version = 1; /* 3. 註冊 */ ret = input_register_device(input_emulator_dev); if (ret < 0) { return -1; } return 0; } static void input_emulator_exit(void) { input_unregister_device(input_emulator_dev); input_free_device(input_emulator_dev); } module_init(input_emulator_init); module_exit(input_emulator_exit); MODULE_LICENSE("GPL");
KERN_DIR = /media/sourcelink/Backups/5.CompileCode/firefly3288/kernel all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order obj-m += smart_remote.o
編譯完後,放到板子上加載,使用命令cat /proc/bus/input/devices
查看設備加載狀況以下:瀏覽器
root@firefly:/data/nfs # ins insmod installd root@firefly:/data/nfs # insmod smart_remote.ko root@firefly:/data/nfs # cat /proc/bus/input/devices .... I: Bus=0001 Vendor=0001 Product=0010 Version=0001 N: Name="smart remote" P: Phys= S: Sysfs=/devices/virtual/input/input3 U: Uniq= H: Handlers=sysrq event3 ddr_freq keychord B: PROP=0 B: EV=100003 B: KEY=ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
如上是我板子加載完驅動的狀況,原默認的信息我已經刪除, 也能夠看下在<font color=red>/dev/input</font>, 節點下多了個<font color=red>event3</font>;工具
root@firefly:/data/nfs # ls /dev/input/ event0 event1 event2 event3
這樣就完成一個輸入設備的模擬,接下來介紹兩個工具模擬事件的產生;佈局
android系統提供了<font color=red>getevent</font> 與<font color=red>sendevent</font>兩個工具供開發從設備節點中讀取和寫入事件;測試
語法:spa
getevent [-opera] [節點路徑]
可使用getevent -help
查看一些具體的operation; 翻譯
若是不帶指定設備節點, 這樣會監控全部的設備節點:3d
root@firefly:/data/nfs # getevent add device 1: /dev/input/event3 name: "smart remote" add device 2: /dev/input/event2 name: "RK_ES8323 Headphone Jack" add device 3: /dev/input/event1 name: "rk29-keypad" add device 4: /dev/input/event0 name: "ff680000.pwm"
如今我將本身的鍵盤插到開發板上了,使用getevent
命令來監控事件;
當我按下並鬆開鍵盤上的數字1
鍵獲取到的數據以下:
root@firefly:/data/nfs # getevent /dev/input/event4 0004 0004 0007001e 0001 0002 00000001 0000 0000 00000000 0004 0004 0007001e 0001 0002 00000000 0000 0000 00000000
數據意義依次是: 事件類型, 事件代碼, 事件值
事件值的1表示按下, 0表示擡起, 觀察數據能夠發現每次按下或擡起都會獲取到0000 0000 00000000
的數據, 表示同步事件,通知這次事件已經結束能夠進行處理了;
這裏的事件代碼是linux端發來的原始數據, 在android端還會進行一次鍵值佈局,下面筆者會講解這個映射關係;
語法:
setevent [節點路徑] [事件類型] [事件代碼] [事件值]
如今操做下往設備幾點寫入一個事件, 打開我開發板的瀏覽器:
依次輸入以下指令:
sendevent /dev/input/event3 1 2 1 sendevent /dev/input/event3 1 2 0 sendevent /dev/input/event3 0 0 0
效果以下:
屏幕上出現了一個數字1, 最後發送的0 0 0
表示同步事件,通知這次事件已經結束能夠進行處理了.
按鍵事件來源於linux內核, 可是在android端對按鍵的值有從新作一個佈局,即將linux key code轉換爲android key code, 這個佈局依賴於個.kl
文件, 全稱: Key Layout Files;
該文件搜索路徑以下:
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl
/odm/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/vendor/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl
/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl
/odm/usr/keylayout/DEVICE_NAME.kl
/vendor/usr/keylayout/DEVICE_NAME.kl
/system/usr/keylayout/DEVICE_NAME.kl
/data/system/devices/keylayout/DEVICE_NAME.kl
/odm/usr/keylayout/Generic.kl
/vendor/usr/keylayout/Generic.kl
/system/usr/keylayout/Generic.kl
/data/system/devices/keylayout/Generic.kl
從上面路徑信息能夠看出Key Layout Files文件的命名和廠家信息有關, 具體爲供應商id, 產品id和設備名有關;
好比咱們的模擬輸入設備的須要的.kl
文件能夠命名爲Vendor_0001_Product_0010.kl
或smart remote.kl
;
輸入系統在檢測到有新設備接入時, 在上述路徑查找對應符合規則的.kl
文件並加載它,若是沒有找到則加載Generic.kl
文件;
拷貝一個Generic.kl
和咱們模擬按鍵設備名字同樣, 並加上權限,若是你的板子上沒有該目錄的話則建立它;
cp /system/usr/keylayout/Generic.kl /data/system/devices/keylayout/smart_remote.kl chmod 777 /data/system/devices/keylayout/smart_remote.kl
咱們打開這個文件看下里面的內容:
key 1 ESCAPE 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 ....
看到這就明白了爲何咱們前面輸入的鍵值2,最後在瀏覽器上看到了1; 咱們修改下這個文件:
key 1 ESCAPE key 2 3 ...
卸載驅動, 再從新加載驅動後,再依次輸入以下指令:
sendevent /dev/input/event3 1 2 1 sendevent /dev/input/event3 1 2 0 sendevent /dev/input/event3 0 0 0
效果以下:
這樣就達到了輸入一樣按鍵卻獲得不一樣之的效果了;
負責將android key code與修飾符的組合映射到Unicode字符。
/odm/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX_Version_XXXX.kcm
/odm/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/vendor/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/system/usr/keychars/Vendor_XXXX_Product_XXXX.kcm
/data/system/devices/keychars/Vendor_XXXX_Product_XXXX.kcm
/odm/usr/keychars/DEVICE_NAME.kcm
/vendor/usr/keychars/DEVICE_NAME.kcm
/system/usr/keychars/DEVICE_NAME.kcm
/data/system/devices/keychars/DEVICE_NAME.kcm
/odm/usr/keychars/Generic.kcm
/vendor/usr/keychars/Generic.kcm
/system/usr/keychars/Generic.kcm
/data/system/devices/keychars/Generic.kcm
/odm/usr/keychars/Virtual.kcm
/vendor/usr/keychars/Virtual.kcm
/system/usr/keychars/Virtual.kcm
/data/system/devices/keychars/Virtual.kcm
輸入系統在建立設備時會在這些路徑下查找對應的.kl
和.kcm
文件進行加載, 若是沒有找到對應的文件將會加載Generic.kl
和Generic.kcm
文件
咱們如今根據咱們的模擬設備來更改下kcm文件, 操做以下:
mkdir /data/system/devices/keychars cp /system/usr/keychars/Generic.kcm /data/system/devices/keychars/smart_remote.kcm
打開該文件查看下里面內容:
### Basic QWERTY keys ### key A { label: 'A' base: 'a' shift, capslock: 'A' } key B { label: 'B' base: 'b' shift, capslock: 'B' } key C { label: 'C' base: 'c' shift, capslock: 'C' alt: '\u00e7' shift+alt: '\u00c7' } ....
以按鍵<font color=red>A</font>爲例, 查看.kl
文件當輸入事件代碼爲30
時, 對應到android的key A
,
根據.kcm
文件知道此時會映射成字符a
, 當按下shift
鍵時再按下A
會顯示字符A
;
輸入以下指令看下效果:
sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
爲了方便你們查看都使用了文件前面的內容進行修改並演示;
若是想當按下a鍵時顯示字符b, 按下shift+a時顯示字符2修改下kcm文件,以下:
key A { label: 'A' base: 'b' shift, capslock: '2' } ...
從新卸載驅動並加載驅動, 再次執行:
sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
如今試下同時按下shift鍵的效果, 依次輸入以下:
sendevent /dev/input/event3 1 42 1 sendevent /dev/input/event3 1 30 1 sendevent /dev/input/event3 1 30 0 sendevent /dev/input/event3 0 0 0
效果以下:
果真和咱們修改的kcm文件映射的字符保持了一致;
按鍵事件的轉化流程大體以下圖:
若是想個性化定製輸入的按鍵的鍵值和字符顯示只須要修改kl和kcm文件;