Analyze how Android reads keys and touch panel drivers. This is mainly in the $ (android_dir)/frameworks/base/libs/UI/eventhub. cpp file. This is at the Hal layer, which will step by step analyze how the android upper layer accepts events. First, let's take a look at Android halclass eventhub at $ (android_dir)/frameworks/base/include/UI/eventhub. h definition. i. scan_dir (const char * dirname) // dirname = "/dev/input" scans the dirname Directory, which contains event0, event1 ..., and other devices. II. open_device (devname); Enable/dev/input/event0,/dev/input/event1, and other devices. the following uses the/dev/input/event0 device as an example to analyze the underlying processing of the buttons. for (attempt = 0; attempt <10; attempt ++ ){
FD = open (devicename, o_rdwr );
If (FD> = 0) break;
Usleep (100 );
} The uploaded device will be opened first. the system then obtains the version, ID, and other information. if (IOCTL (FD, eviocgname (sizeof (name)-1), & name) <1 ){
// Fprintf (stderr, "cocould not get device name for % s, % s/n", devicename, strerror (errno ));
Name [0] = '/0 ';
}
Get the driver name, which is/dev/input/evevnt0 here, that is, read it in the driver. this name is very important and will be matched with the keyboard map later. here, the returned value is: Name = "wayland_m_ebook_key_input". Why does it return this value? See the following statement in the Linux driver. wayland_m_ebook_keypad_probe () function of event0: gpio_key_input-> name = "wayland_m_ebook_key_input". Therefore, this value is set at this time. Int devid = 0;
While (devid <mnumdevicesbyid ){
If (mdevicesbyid [devid]. device = NULL ){
Break;
}
Devid ++;
}
If (devid> = mnumdevicesbyid ){
Device_ent * new_devids = (device_ent *) realloc (mdevicesbyid,
Sizeof (mdevicesbyid [0]) * (devid + 1 ));
If (new_devids = NULL ){
LogE ("out of memory ");
Return-1;
}
Mdevicesbyid = new_devids;
Mnumdevicesbyid = devid + 1;
Mdevicesbyid [devid]. device = NULL;
Mdevicesbyid [devid]. seq = 0;
} Allocate a new device and save the device information to the mdevicebyid [] array. mnumdevicesbyid: number of devices mdevicesbyid: devive information new_mfds = (pollfd *) realloc (MFDs, sizeof (MFDs [0]) * (mfdcount + 1 ));
New_devices = (device_t **) realloc (mdevices, sizeof (mdevices [0]) * (mfdcount + 1 ));
Allocate space for new_mfds and MFDs to save the FD. MFDs [mfdcount]. FD = FD for each event (X;
MFDs [mfdcount]. Events = Pollin;
Put FD in the MFDs array. // see if this is a keyboard, and classify it.
Uint8_t key_bitmask [(key_max + 1)/8];
Memset (key_bitmask, 0, sizeof (key_bitmask ));
Logv ("getting keys ...");
If (IOCTL (FD, eviocgbit (ev_key, sizeof (key_bitmask), key_bitmask)> = 0 ){
// Logi ("map/N ");
// For (INT I = 0; I <(key_max + 1)/8); I ++ ){
// Logi ("% d: 0x % 02x/N", I, key_bitmask [I]);
//}
For (INT I = 0; I <(btn_misc + 7)/8); I ++ ){
If (key_bitmask [I]! = 0 ){
Device-> classes | = class_keyboard;
Break;
}
}
If (device-> classes & class_keyboard )! = 0 ){
Device-> keybitmask = new uint8_t [sizeof (key_bitmask)];
If (device-> keybitmask! = NULL ){
Memcpy (device-> keybitmask, key_bitmask, sizeof (key_bitmask ));
} Else {
Delete device;
LogE ("out of memory allocating key bitmask ");
Return-1;
}
}
}
If it is a keyboard device. If (test_bit (btn_touch, key_bitmask )){
Uint8_t abs_bitmask [(abs_max + 1)/8];
Memset (abs_bitmask, 0, sizeof (abs_bitmask ));
Logv ("getting absolute controllers ...");
If (IOCTL (FD, eviocgbit (ev_abs, sizeof (abs_bitmask), abs_bitmask)> = 0)
{
If (test_bit (abs_x, abs_bitmask) & test_bit (abs_y, abs_bitmask )){
Device-> classes | = class_touchscreen;
}
} Continue to analyze how Android maps keyboard: 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 ");
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;
} Android_root is generally set to/system tmpfn, that is, the content in name. The keylayoutfilename =/system/usr/keylayout/wayland_m_ebook_key_input.kl file contains key information. You can modify the key value of the key in this file. If this file is not available, the system reads define Qwerty. KL. device-> layoutmap-> load (keylayoutfilename); load/system/usr/keylayout/wayland_m_ebook_key_input.kl file for analysis. Keylayoutmap: load is implemented in keylayoutmap. cpp. Save the key ing relationships in: keyedvector <int32_t, key> m_keys. Iii. eventhub: getevent () mainly uses the read () function to read key events and map key values. Ii. Let's take a look at JNI layer com_android_server_keyinputqueue.cpp and check several lines of code: static jninativemethod ginputmethods [] = {
/* Name, signature, funcptr */
{"Readevent", "(landroid/View/rawinputevent;) Z ",
(Void *) android_server_keyinputqueue_readevent}. We can see that the upper layer (framework layer) calls readevent and the implementation function is android_server_keyinputqueue_readevent () of this file. This function calls getevent to read events. That is, eventhub: getevent (). readevent in eventhub. cpp calls hub-> getevent to read the event and convert it to a Java structure. 3. Check the Java layer corresponding to the JNI layer (often encapsulated. JAR file) keyinputqueue. java in frameworks/base/services/Java/COM/Android/Server/keyinputqueue. an inputdevicereader thread is created in Java to read events cyclically and put the events into the event queue, including key/touch panel.
Iv. Event Distribution
Input the event distribution thread in frameworks/base/services/Java/COM/Android/Server/windowmanagerservice. java creates an input event distribution thread that distributes events to corresponding windows.
Button touch screen process analysis:
Constructor of the windowmanagerservice class
Windowmanagerservice ()
Mqueue = new keyq ();
Because windowmanagerservice. Java (frameworks/base/services/Java/COM/Android/Server) contains:
Private class keyq extends keyinputqueue implements keyinputqueue. filtercallback
Keyq is the implementation of the abstract class keyinputqueue. Therefore, in the new keyq class, an inputdevicereader thread is actually created in the keyinputqueue class to read key events from devices.
Code:
{
Function onclick ()
{
Function onclick ()
{
Cnblogs_code_show ('62132c69-fd85-40d9-94ab-bfc6127ab80d ')
}
}
} "> Thread mthread = new thread ("inputdevicereader "){
Public void run (){
// Call in a loop: readevent (EV );
...
Send = preprocessevent (Di, Ev );
// The preprocessevent function of the keyq class is actually called.
...
Int keycode = rotatekeycodelocked (EV. keycode );
Int [] map = mkeyrotationmap;
For (INT I = 0; I <n; I + = 2 ){
If (MAP = keycode)
Return map [I + 1];
}//
Addlocked (Di, curtime, Ev. Flags, rawinputevent. class_keyboard,
Newkeyevent (Di, Di. mdowntime, curtime, down, keycode, 0, scancode ,...));
Queuedevent EV = obtainlocked (device, when, flags, classtype, event );
}
};
Readevent () is actually called in com_android_server_keyinputqueue.cpp (frameworks/base/services/JNI)
Static jboolean android_server_keyinputqueue_readevent (jnienv * ENV, jobject clazz, jobject event) to read the event,
Bool res = hub-> getevent (& DeviceID, & type, & scancode, & keycode, & flags, & Value, & When) calls eventhub. in CPP (frameworks/base/libs/UI:
Bool eventhub: getevent (int32_t * outdeviceid, int32_t * outtype,
Int32_t * outscancode, int32_t * outkeycode, uint32_t * outflags,
Int32_t * outvalue, nsecs_t * outwhen)
The Read Device operation is called in the function: res = read (MFDs. FD, & IEV, sizeof (IEV ));
After the constructor windowmanagerservice () calls New keyq (), it calls:
Minputthread = new inputdispatcherthread ();
...
Minputthread. Start ();
To start a thread, inputdispatcherthread.
Run ()
Process ();
Queuedevent EV = mqueue. getevent (...)
Because in the windowmanagerservice class: Final keyq mqueue;
Therefore, the inputdispatcherthread actually reads the key event from the keyq event queue and processes the event in the process () method.
Switch (EV. classtype)
Case rawinputevent. class_keyboard:
...
Dispatchkey (keyevent) eV. event, 0, 0 );
Mqueue. recycleevent (EV );
Break;
Case rawinputevent. class_touchscreen:
// Log. I (TAG, "read next event" + eV );
Dispatchpointer (EV, (motionevent) eV. event, 0, 0 );
Break;
Case rawinputevent. class_trackball:
Dispatchtrackball (EV, (motionevent) eV. event, 0, 0 );
Break;
========================================================== ======================================
Some additional content:
When writing a program, you need to capture the keycode_home, keycode_endcall, keycode_power buttons, but these buttons are specially processed by the system,
Some operations were done before the dispatch. Besides keygaurd, home is not sent to any other app. The endcall and power are similar, so we need the system
Process before processing.
My practice is to define a flag by myself, add this flag to my program, and then obtain the flag attribute of the current window in windowmanagerservices. java. If it is my
The flag we set is not specially processed. We directly distribute the key messages to our app for processing by the app.
It is best to add this part of code in
@ Override
Boolean preprocessevent (inputdevice device, rawinputevent event)
Method, this method is a virtual function in keyinputqueue, a "preprocessing" before processing the key event ".
PS: the processing of the Home key seems to have to modify the interceptkeyti method in phonewindowmanager. java. For details, refer to the processing of the keyguard program.