1. 程式人生 > >android Input子系統分析

android Input子系統分析



Input Technical Information

Android 輸入子系統支援許多不同的裝置類,包括鍵盤,搖桿,軌跡球,滑鼠和觸控式螢幕.

這份文件描述了上層如何配置,校準,測試,和編寫輸入裝置驅動.

Input Concepts

Input Device Classes

Keyboard Devices

Touch Devices

Tools

Dumpsys

Getevent

Validate Keymaps

Overview

Android 輸入子系統是由一條貫穿系統多層的事件管道線組成的.

Input Pipeline

在底層,物理輸入裝置會通過產生電子訊號,比如按鍵或者觸控,來表示裝置的狀態改變了。接著裝置韌體會將這些訊號編碼並通過USB HID或者通過I2C匯流排產生中斷上報給系統.

Linux核心收到這些訊號後會通過裝置驅動來解碼。Linux核心為許多標準的外圍裝置特別是標準的HID協議提供了驅動。但如果是OEM的話,則需要客戶自己為嵌入式裝置提供底層驅動,就像比較常見的觸控式螢幕一樣.

Linux輸入裝置驅動通過linux輸入協議,負責將裝置特定的訊號轉換為標準的輸入事件格式。Linux輸入協議定義了一個標準的事件型別集,在程式碼的/include/linux/input.h

標頭檔案裡面。就這樣,核心外圍的輸入裝置驅動部分就可以不用管像 scan codes HID usages I2C messagesGPIO pin 之類的物理特性了.

然後,Android EventHub 通過開啟關聯到裝置上的事件裝置驅動,讀取輸入裝置事件。Android InputReader 接著根據裝置類和Android輸入事件流來解析輸入事件。在這個過程中,linux協議事件碼會通過輸入裝置的配置檔案(*.idc),鍵盤的佈局檔案(*.kl),已經各種對映表(*.kcm)轉換為Android事件碼.

最終,InputReader把輸入事件傳送到InputDispatcher

InputDispatcher再將事件傳送到合適的視窗.

Control Points

在輸入管道線上,有幾個地方會影響輸入我們控制裝置的行為

Driver and Firmware Configuration

輸入裝置驅動或者韌體本身頻繁地設定裝置暫存器引數或者配置裝置的行為。特別是像嵌入式觸控式螢幕這種裝置,需要通過不停地更改引數來校準,或者為了更高的精確度,或者為了抑制噪音,頻繁地調整韌體的各種引數。

在核心的BSP中,常常將驅動配置選項指定為模組引數,這樣的話一個驅動就可以支援多個不同的硬體平臺。

Board Configuration Properties

InputReader 可能會通過SysFS用到核心BSP裡面設定的開發板配置屬性,比如觸控式螢幕的虛擬按鍵的佈局。

Resource Overlays

少數輸入行為是通過在config.xml進行資源覆蓋來,比如一些開關效果。

下面是一些例子:

·config_lidKeyboardAccessibility: 當實體鍵盤訪問和隱藏的時候的特殊效果

·config_lidNavigationAccessibility: 當觸控板被訪問和隱藏時候的特殊效果

·config_longPressOnPowerBehavior: 當用戶按下power鍵時候的特殊效果

·config_lidOpenRotation: 當螢幕的方向改變時候的特殊效果

這裡的每個配置選項的詳細配置可以檢視framworks/base/core/res/res/values/config.xml.

Key Maps

鍵盤對映用於Androdi EventHubInputReader用來配置Linux按鍵、遙控杆方向、遙控手柄按鍵的事件碼到Android事件碼,對映方法可能會因裝置或者語言不同而有所不同。

Input Device Configuration Files

輸入裝置配置檔案提供給Android EventHubInputReader配置特殊的裝置特性,比如有多少觸控資訊需要報告。

Understanding HID Usages and Event Codes

鍵盤、遊戲控制器的按鍵、搖桿或其他控制裝置各自有自己的裝置識別符號,這些識別符號之間不一定都是形同的,他們依靠的對映表,有些裝置有些按鍵可以互用,但大多時候受裝置的特性、裝置的驅動、不同的國家地區或者區域、系統配置、使用者偏好還有其他因素影響。

Physical Scan Code

Scancode 是裝置上每個按鍵,按鈕或者其他控制連線到裝置上的特殊標誌符。因為scancode在不同是裝置之間常常都是不同的,即同一個按鈕在不同的裝置上,對應的scancode大多都是不相同的,因此需要韌體或者裝置驅動來負責將這些scancode對映為標準的標誌符,比如標準的HID裝置或者linux裝置的keycode

Scancode主要用在鍵盤上。或者是其他通過GPIOi2c、或者其他通訊方式的裝置,

HID Usage

HID Usage是一個標準的識別符號,用於報告一些控制的狀態,如鍵盤鍵,搖桿軸,滑鼠鍵或觸控接觸點.大多數USB和藍芽輸入裝置符合HID規範,因此他們都能用相同的方式和系統銜接.

Android Framework 依靠Linux kernel HID 驅動來轉換HID usage codes為linux keycodes或者其他識別符號.因此HID usage對廠商很有吸引力.

Linux Key Code

一個Linux key code是一個標準的按鍵或按鈕的識別符號.Linux key code是定義在的linux/Input.h標頭檔案,用字首的KEY_或BTN_的開頭來表示的常量. Linux核心輸入驅動負責轉換物理掃描碼,HID用法和其他特定裝置的訊號為Linuxkey code,併發送和EV_KEY相關的資訊.

Android的API有時引用"scan code"來表示Linux key code.從技術上講這是不正確的,但它有助於區分Linuxkey code 和Android的API中的key code.

Linux Relative or Absolute Axis Code

Linux relative absolute axis code 是一個標誌的識別符號用來上報一個軸的相關移動或者絕對位置的事件比如一個滑鼠沿X軸或沿X軸移動或者操縱桿的絕對位置的改變. Linux axis code定義在Linux/input.h標頭檔案裡面以字首為REL_ABS_的常量. Linux核心輸入驅動程式負責轉換HID usages和其他特定裝置的訊號為Linuxaxis code,並上報和EV_REL和EV_ABS事件有關的資訊.

Linux Switch Code

Linuxswitch code 是一個標準的識別符號,用來上報一個裝置的開關事件,比如一個合蓋的開關,Linux switch code定義在Linux/input.h標頭檔案裡面SW_開頭的常量.Linux kernel 輸入驅動把開關狀態改變的事件作為EV_SW事件上報.

Android應用程式通常不從開關接收事件,但該系統可以通過使用它們實現各種裝置需要的功能.

Android Key Code

Android Key Code 是一個定義在Android API裡面的標準的識別符號,比如“HOME”按鍵的HOME就是一個識別符號.Android Key Code是定義在android.view.KeyEvent 類,以KEYCODE_的一個常量.

鍵佈局指定一個Linux key code 如何對映為Android key code,鍵佈局之間的差異和鍵的模式,語言,國家,佈局和特殊的功能.

Android key codes的組合轉換為字元碼需要用本地指定的鍵字元對映.例如,當一個按鍵標識為KEYCODE_SHIFTKEYCODE_A同時按下,系統先在字元對映查詢組合,並查詢首字母A,然後在當前游標處插入對映檔案指定的文字字元.

Android Axis Code

Android axis code 是一個標準的識別符號定義在Android API,用來表示一個特定的裝置軸.Android axis codes 是定義在android.view.MotionEvent類,一個以KEYCODE_開頭的常量.

鍵佈局指定了Linux Axis codes是如何對映為Android axis codes. 鍵佈局之間的差異和鍵的模式,語言,國家,佈局和特殊的功能.

Android Meta State

Android Meta State 是一個標準的識別符號,定義在Android API 裡面,用來指定那個修飾鍵被按下. Android meta states 定義在android.view.KeyEvent類,以META_開頭的常量.

當類似KEYCODE_SHIFT_LEFT修飾鍵被pressed/released,或者sets/resetsAndroid InputReader 元件會確定meta state.

修飾鍵和meta state直接的關係是硬體確定的,但可以通過修改key layout來影響meta states.

Android Button State

Android button State 是一個標準的識別符號,定義在Android API 裡面,用來指定哪個按鈕(滑鼠或者筆尖)被按下. Android button states 定義在android.view.MotionEvent類,以BUTTON_開頭的常量.

當按鈕(滑鼠或者筆尖)被pressed/released,或者sets/resetsAndroid InputReader 元件會確定當前button state.

Buttons buttons是由硬體編碼確定的他們之間的關係的.

鍵佈局檔案.kl檔案負責對映linux key code或者axis codeAndroid key code或者axis code並指定相關的屬性標識.

所有按鍵(包含特殊鍵,比如volume、power、media等等)的輸入裝置都需要鍵對映檔案.

特定的裝置不必一定需要鍵佈局檔案,但專用鍵盤和操縱桿建議建立鍵佈局檔案.

如果沒有為裝置建立鍵佈局檔案,那麼系統會選擇一個預設的佈局檔案.

Location

鍵佈局檔名可用USBvendor、product(或者version)id組成的字串,或使用輸入裝置名稱加上字尾來.kl.

系統根據一定的順序來查詢.kl檔案,下面路徑是系統查詢kl檔案的順序:

/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl

/system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl

/system/usr/keylayout/DEVICE_NAME.kl

/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl

/data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl

/data/system/devices/keylayout/DEVICE_NAME.kl

/system/usr/keylayout/Generic.kl

/data/system/devices/keylayout/Generic.kl

當上面的檔案中有一個查詢到以後,系統會將除0-9、A-Z、a-z、-或者"外的其他字元用'代替.

GenericKey Layout File (通用鍵佈局檔案)

Generic.kl檔案是Android為了支援各種各樣的標準鍵盤和操縱桿,內建的一個通用鍵佈局檔案.

請不要修改通用鍵佈局!

Syntax (鍵佈局檔案語法)

鍵佈局檔案是一個純文字的檔案,包含有按鍵和軸的宣告和標識.

Key Declarations (鍵宣告)

鍵宣告由key開頭,緊接著linuxkey code(即scan code),接著是Android key code,在後面可以選擇性地新增一些鍵的屬性.

key 1ESCAPE

key 114VOLUME_DOWNWAKE

key 16QVIRTUAL WAKE

下面是系統可識別的屬性識別符號:

WAKE: 當鍵按下後,裝置會從睡眠中喚醒。因為歷史原因,這個標識的行為和下面的WAKE_DROPPED一樣.

WAKE_DROPPED: 當鍵按下後,裝置會從睡眠中喚醒,但按鍵本身在喚醒發生後會丟失,即按鍵只喚醒裝置,但按鍵本身並不處理.

SHIFT: 表示這個鍵需要在SHIFT鍵按下後才會被解析.

CAPS_LOCK: 表示這個鍵需要在CAPS LOCK鍵按下後才會被解析.

ALT: 表示這個鍵需要在ALT鍵按下後才會被解析.

ALT_GR: 表示這個鍵需要在RIGHT ALT鍵按下後才會被解析.

FUNCTION: 表示這個鍵需要在FUNCTION鍵按下後才會被解析.

VIRTUAL: 表示這個鍵是一個虛擬的按鍵(電容鍵),這和主觸控式螢幕密切相關。這個會觸發特殊抖動處理邏輯,看下面.

MENU: 已過時的屬性,不要使用.

LAUNCHER: 已過時的屬性,不要使用.

AxisDeclarations (軸宣告)

AxisDeclarations 由關鍵字axis,後面緊接linux 軸碼數,後面緊接著至少一個android 軸碼名來控制軸的行為.

Basic Axes(基軸)

基軸簡單地將linux 軸碼對映為android的軸碼名.

下面的宣告對映ABS_X(圖示為0x00)為AXIS_X(圖示為X)

axis 0x00 X

上面的例子中,如果ABS_X是5,那麼AXIS_X將設為5.

Split Axes(分割軸)

一個分割軸對映linux 軸碼為兩個android軸碼名,比如這個值小於或者大於閥值時,那麼對映的時候會對映為兩個不同的軸.當裝置只上報了一個物理軸時會解碼為兩個不同的互斥的邏輯軸.

下面的宣告為當ABS_Y(圖示為0x01)值小於0x7f時,對映為AXIS_GAS;大於0x7f時對映為AXIS_BRAKE.

axis 0x01 split 0x7f GAS BRAKE

上面的例子中,如果ABS_Y的值是0x7d,那麼AXIS_GAS設為2AXIS_BRAKE設為0.相反地,如果ABS_Y值是0x83,則AXIS_GAS被設定為0,並且AXIS_BRAKE設定為4(0×83-0x7f)中。最後,如果該值的ABS_Y等於0x7f的分割值的,那麼AXIS_GAS和AXIS_BRAKE都被設定為0。

Inverted Axes(倒置軸)

倒置軸將得到的軸值的符號取反.

下面的宣告對映ABS_RZ0X05表示AXIS_BRAKEBRAKE表示),並反轉否定它然後輸出

axis 0x05 invert AXIS_RZ

在上面例子中如果ABS_RZ值是2那麼AXIS_RZ被設定為-2

Center Flat PositionOption(中心位置選項)

Linux輸入協議為輸入裝置驅動程式提供了一種方法用來指定搖桿軸的中心位置但不是所有的裝置其中一些裝置提供的是不正確的值。

中心位置指的是軸在一個範圍區間的中心位置,就像一個方向盤,當用戶不觸控它的時候,它預設的軸位置就是中心位置.

要解決這個問題,後面可以跟一個選項用來為軸指定中心位置的值.

axis 0x03 Z flat 4096

上面的例子中,Z軸的中心位置指定為4096.

Comments(註釋)

註釋行以''開始的繼續行結束。像這樣

# A comment!

空行會被忽略。

Examples

Keyboard (鍵盤)

# This is an example of a key layout file for a keyboard.

key 1ESCAPE

key 21

key 32

key 43

key 54

key 65

key 76

key 87

key 98

key 109

key 110

key 12MINUS

key 13EQUALS

key 14DEL

# etc...

System Controls (系統控制)

# This is an example of a key layout file for basic system controls, such as

# volume and power keys which are typically implemented as GPIO pins that

# the device decodes into key presses.

key 114VOLUME_DOWNWAKE

key 115VOLUME_UPWAKE

key 116POWERWAKE

Capacitive Buttons (電容按鈕)

# This is an example of a key layout file for a touch device with capacitive buttons.

key 139MENUVIRTUAL

key 102HOMEVIRTUAL

key 158BACKVIRTUAL

key 217SEARCHVIRTUAL

Headset Jack MediaControls (耳機插孔媒體控制)

# This is an example of a key layout file for headset mounted media controls.

# A typical headset jack interface might have special control wires or detect known

# resistive loads as corresponding to media functions or volume controls.

# This file assumes that the driver decodes these signals and reports media

# controls as key presses.

key 163MEDIA_NEXTWAKE

key 165MEDIA_PREVIOUSWAKE

key 226HEADSETHOOKWAKE

Joystick (搖桿)

# This is an example of a key layout file for a joystick.

# These are the buttons that the joystick supports, represented as keys.

key 304BUTTON_A

key 305BUTTON_B

key 307BUTTON_X

key 308BUTTON_Y

key 310BUTTON_L1

key 311BUTTON_R1

key 314BUTTON_SELECT

key 315BUTTON_START

key 316BUTTON_MODE

key 317BUTTON_THUMBL

key 318BUTTON_THUMBR

# Left and right stick.

# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.

# This confuses applications that rely on the flat value because the joystick actually

# settles in a flat range of +/- 4096 or so.We override it here.

axis 0x00 X flat 4096

axis 0x01 Y flat 4096

axis 0x03 Z flat 4096

axis 0x04 RZ flat 4096

# Triggers.

axis 0x02 LTRIGGER

axis 0x05 RTRIGGER

# Hat.

axis 0x10 HAT_X

axis 0x11 HAT_Y

WakeKeys

喚醒鍵是特殊的按鍵,可以將裝置從睡眠中喚醒,比如power按鍵.

預設情況下,內部鍵盤裝置,沒有按鍵是一個喚醒鍵.對於外部鍵盤裝置,所有的按鍵都是喚醒鍵.

注意WindowManagerPolicy元件是負責執行喚醒鍵的行為。此外,按鍵保護可能會阻止某些鍵的功能為喚醒鍵。瞭解喚醒鍵的行為的最好的地方在PhoneWindowManager.interceptKeyBeforeQueueing.

VirtualSoft Keys

輸入系統提供了特殊的功能來實現虛擬的軟鍵.

有下面三種情況:

1、如果以圖形的方式將軟鍵顯示在螢幕上,比如在Galaxy Nexus,則圖形介面的實現在System UI package裡面的Navigation Bar元件實現.

因為圖形虛擬軟鍵在系統的上層實現,沒有包含鍵盤的佈局檔案,所以下面的資訊沒有應用.

2、如果虛擬的軟鍵用主觸控式螢幕的一個擴充套件的可觸控區域來實現例如在Nexus One會用虛擬按鍵對映檔案轉換X / Y觸控座標為Linux key code然後用按鍵佈局檔案翻譯Linux key codeAndroid key code.

檢視TouchDevices 章節檢視更多有關虛擬按鍵對映檔案.

觸控式螢幕輸入裝置的按鍵佈局檔案必須指定相應的鍵對映,併為每個鍵加上VIRTUAL標識.

3、如果虛擬軟鍵是電容按鍵,通過從主螢幕分出一塊區域來來實現的,例如在Nexus S上,核心裝置驅動或者韌體負責轉換觸控為linux key code ,然後通過輸入子系統用鍵佈局檔案將其轉換為androidkey code.

輸入裝置電容按鍵的鍵對映檔案必須指定相應的按鍵對映併為每個鍵新增VIRTUAL標識.

當虛擬軟鍵的觸控式螢幕和我們人體近距離接觸時,或者手指從屏的上面到下面滑動,或者從下面到上面滑動時,使用者很容易按到螢幕上的電容按鈕.

為了防止這種情況的發證,輸入系統通過去抖的方法,比如虛擬軟鍵按下後,如果時間很短,虛擬軟鍵的按鍵會被忽略掉.這段有效的時間即為virtual key quiet time.

開啟虛擬軟鍵防抖,我們需要做以下兩件事.

第一,我們需要為觸控式螢幕或者電容按鍵輸入裝置提供一個鍵佈局檔案,併為每個鍵新增VIRTUAL標識.

key 139MENUVIRTUAL

key 102HOMEVIRTUAL

key 158BACKVIRTUAL

key 217SEARCHVIRTUAL

然後,我們在frameworkconfig.xml裡面設定virtual key quiet time

<!-- Specifies the amount of time to disable virtual keys after the screen is touched

in order to filter out accidental virtual key presses due to swiping gestures

or taps near the edge of the display.May be 0 to disable the feature.

It is recommended that this value be no more than 250 ms.

This feature should be disabled for most devices. -->

<integer name="config_virtualKeyQuietTimeMillis">250</integer>