日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 中input event的分析

發布時間:2025/3/20 Android 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 中input event的分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

文章將分析Android 的Input Event 子系統的來龍去脈。

?

Android 系統里面有很多小工具,運行這些工具,我們對它們有一個感性的認識,進而閱讀和分析這些小工具源代碼,再順藤摸瓜,就可以把整個子系統的來龍去脈弄清楚。

?

1.運行toolbox的getevent 工具。

?

# getevent -help
getevent -help
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-p] [-q] [-c count] [-r] [device]
??? -t: show time stamps
??? -n: don't print newlines
??? -s: print switch states for given bits
??? -S: print all switch states
??? -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32)
??? -p: show possible events (errs, dev, name, pos. events)
??? -q: quiet (clear verbosity mask)
??? -c: print given number of events then exit
??? -r: print rate events are received
# getevent -c 20
getevent -c 20
add device 1: /dev/input/event4
? name:???? "sensor-input"
add device 2: /dev/input/event3
? name:???? "88pm860x_hook"
add device 3: /dev/input/event2
? name:???? "88pm860x_on"
add device 4: /dev/input/event1
? name:???? "88pm860x-touch"
add device 5: /dev/input/event0
? name:???? "pxa27x-keypad"
/dev/input/event0: 0001 0066 00000001
/dev/input/event0: 0000 0000 00000000
/dev/input/event0: 0001 0066 00000000
/dev/input/event0: 0000 0000 00000000
/dev/input/event1: 0003 0000 00000c48
/dev/input/event1: 0003 0001 00000751
/dev/input/event1: 0001 014a 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0000 00000c67
/dev/input/event1: 0003 0001 000006f9
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0000 00000c9e
/dev/input/event1: 0003 0001 0000069e
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0000 00000cc4
/dev/input/event1: 0003 0001 00000620
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0003 0000 00000ce8
/dev/input/event1: 0003 0001 000005ba
/dev/input/event1: 0000 0000 00000000

運行這個工具,然后按鍵或者滑動觸摸屏,會看到程序會實時打印event。從上面的輸出來看,系統有5個input 子系統。它們分別是

?

add device 1: /dev/input/event4
? name:???? "sensor-input"

#Sensor input 子系統

?

add device 2: /dev/input/event3
? name:???? "88pm860x_hook"

#耳機Hook鍵子系統。可支持接電話掛電話的耳機上面有一個按鍵,對應的就是這個input 子系統。


add device 3: /dev/input/event2
? name:???? "88pm860x_on"

?

#開機鍵 input 子系統
add device 4: /dev/input/event1
? name:???? "88pm860x-touch"

#Touch Screen input 子系統


add device 5: /dev/input/event0
? name:???? "pxa27x-keypad"

#按鍵子系統,包括Home/Menu/Back等按鍵。

?

可以嘗試多種event,實際感覺一下出來的log。

?

?

2.閱讀getevent的代碼。代碼為./core/toolbox/getevent.c

?

從代碼中,我們知道,程序在while(1)的一個死循環里,不斷地在讀取 (select 操作)/dev/input?下面的文件,檢查是否Kernel往里面更新內容,如果有內容更新,就把它打印出來。并且從代碼中,我們還知道,任何一個event都有三種屬性,type,code,value.

?

?

??? while(1) {
??????? pollres = poll(ufds, nfds, -1);?
??????? //printf("poll %d, returned %d/n", nfds, pollres);
??????? if(ufds[0].revents & POLLIN) {
??????????? read_notify(device_path, ufds[0].fd, print_flags);
??????? }
??????? for(i = 1; i < nfds; i++) {
??????????? if(ufds[i].revents) {
??????????????? if(ufds[i].revents & POLLIN) {
??????????????????? res = read(ufds[i].fd, &event, sizeof(event));?
??????????????????? if(res < (int)sizeof(event)) {
??????????????????????? fprintf(stderr, "could not get event/n");
??????????????????????? return 1;
??????????????????? }
??????????????????? if(get_time) {
??????????????????????? printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec);
??????????????????? }
??????????????????? if(print_device)
??????????????????????? printf("%s: ", device_names[i]);
??????????????????? printf("%04x %04x %08x", event.type, event.code, event.value);

??????????????????? if(sync_rate && event.type == 0 && event.code == 0) {
??????????????????????? int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
??????????????????????? if(last_sync_time)
??????????????????????????? printf(" rate %lld", 1000000LL / (now - last_sync_time));
??????????????????????? last_sync_time = now;
??????????????????? }
??????????????????? printf("%s", newline);
??????????????????? if(event_count && --event_count == 0)
??????????????????????? return 0;
??????????????? }
??????????? }
??????? }

?

?

3.問題來了,Android Framework是否也是一樣的原理呢??猜測應該是一樣的才對,不然這個工具就沒有調試的價值了。

?

我們來閱讀和分析framework中input event的相關代碼。

?

我們從Kernel層往上看,先看看Framework中,直接操縱/dev/input設備的代碼。

?

在.frameworks/base/libs/ui/EventHub.cpp 中,我們看到跟getevent工具類似的代碼。

?

bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
??????? int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
??????? int32_t* outValue, nsecs_t* outWhen)
{

....

?

??? while(1) {
....
??????? release_wake_lock(WAKE_LOCK_ID);

??????? pollres = poll(mFDs, mFDCount, -1);?

??????? acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);

??????? if (pollres <= 0) {
??????????? if (errno != EINTR) {
??????????????? LOGW("select failed (errno=%d)/n", errno);
??????????????? usleep(100000);
??????????? }
??????????? continue;
??????? }

?

....


??????? // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
??????? for(i = 1; i < mFDCount; i++) {
??????????? if(mFDs[i].revents) {
??????????????? LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
??????????????? if(mFDs[i].revents & POLLIN) {
??????????????????? res = read(mFDs[i].fd, &iev, sizeof(iev));?
??????????????????? if (res == sizeof(iev)) {
??????????????????????? LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
???????????????????????????? mDevices[i]->path.string(),

???????????? ....

??????? }

?

4.那么framework中那個模塊再調用EventHub呢,接著往下查。

?

在framework目錄中,輸入下面的命令查找

?

# find . -name "*.cpp" |grep -v EventHub | xargs grep EventHub?
./base/services/jni/com_android_server_KeyInputQueue.cpp:#include <ui/EventHub.h>
./base/services/jni/com_android_server_KeyInputQueue.cpp:static sp<EventHub> gHub;
./base/services/jni/com_android_server_KeyInputQueue.cpp:??? sp<EventHub> hub = gHub;
./base/services/jni/com_android_server_KeyInputQueue.cpp:??????? hub = new EventHub;
./base/services/jni/com_android_server_KeyInputQueue.cpp:??? sp<EventHub> hub = gHub;
./base/services/jni/com_android_server_KeyInputQueue.cpp:??????? hub = new EventHub;

?

5.從查找結果中得知,在jni文件com_android_server_KeyInputQueue.cpp文件中有對EventHub進行調用。


打開并閱讀com_android_server_KeyInputQueue.cpp文件得知,在下面的函數中調用了EventHub的getEvent函數

?

static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
????????????????????????????????????????? jobject event)
?
{
??? gLock.lock();
??? sp<EventHub> hub = gHub;
??? if (hub == NULL) {
??????? hub = new EventHub;
??????? gHub = hub;
??? }
??? gLock.unlock();

??? int32_t deviceId;
??? int32_t type;
??? int32_t scancode, keycode;
??? uint32_t flags;
??? int32_t value;
??? nsecs_t when;
??? bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
??????????? &flags, &value, &when);
?


??? env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
??? env->SetIntField(event, gInputOffsets.mType, (jint)type);
??? env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
??? env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
??? env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
??? env->SetIntField(event, gInputOffsets.mValue, value);
??? env->SetLongField(event, gInputOffsets.mWhen,
??????????????????????? (jlong)(nanoseconds_to_milliseconds(when)));

??? return res;
}

6.根據jni的調用規則,在本文件中查找對于的java函數。

?

static JNINativeMethod gInputMethods[] = {

??? /* name, signature, funcPtr */
??? { "readEvent",?????? "(Landroid/view/RawInputEvent;)Z",
??????????? (void*) android_server_KeyInputQueue_readEvent },

??? ....?

7. 接著順藤摸瓜,找到對應的java文件,base/services/java/com/android/server/KeyInputQueue.java

?

??? private static native boolean readEvent(RawInputEvent outEvent);

在一個線程中會調用readEvent函數。

?

??? Thread mThread = new Thread("InputDeviceReader") {
??????? public void run() {
??????????? if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
??????????? android.os.Process.setThreadPriority(
??????????????????? android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);

??????????? RawInputEvent ev = new RawInputEvent();
??????????? while (true) {
??????????????? try {
??????????????????? InputDevice di;

??????????????????? // block, doesn't release the monitor
??????????????????? readEvent(ev);
?

??????????????????? boolean send = false;
??????????????????? boolean configChanged = false;

??????????????????? if (false) {
??????????????????????? Slog.i(TAG, "Input event: dev=0x"
??????????????????????????????? + Integer.toHexString(ev.deviceId)
??????????????????????????????? + " type=0x" + Integer.toHexString(ev.type)
??????????????????????????????? + " scancode=" + ev.scancode
??????????????????????????????? + " keycode=" + ev.keycode
??????????????????????????????? + " value=" + ev.value);
??????????????????? }

?

8.那是誰啟動這個線程呢???查找mThread變量,得知在KeyInputQueue的構造函數中會啟動這個線程。

?

??? KeyInputQueue(Context context, HapticFeedbackCallback? hapticFeedbackCallback)?{
??????? if (MEASURE_LATENCY) {
??????????? lt = new LatencyTimer(100, 1000);
??????? }

??????? Resources r = context.getResources();
??????? BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);

??????? JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);

??????? mHapticFeedbackCallback = hapticFeedbackCallback;

??????? readExcludedDevices();

??????? PowerManager pm = (PowerManager)context.getSystemService(
??????????????????????????????????????????????????????? Context.POWER_SERVICE);
??????? mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
??????????????????????????????????????????????????????? "KeyInputQueue");
??????? mWakeLock.setReferenceCounted(false);

??????? mFirst = new QueuedEvent();
??????? mLast = new QueuedEvent();
??????? mFirst.next = mLast;

?

??????? mThread.start();?
??? }

9.那這個KeyInputQueue是在哪里被實例化呢?

?

而且查看KeyInputQueue類的聲明,得知它是一個abstract class.

?

public abstract class KeyInputQueue

{

.....

}

說明它肯定會被某個類繼承.接著查找。

?

/frameworks$ find . -name "*.java" |grep -v KeyInputQueue | xargs grep KeyInputQueue?
./policies/base/phone/com/android/internal/policy/impl/KeyguardViewMediator.java: * {@link com.android.server.KeyInputQueue}'s and {@link android.view.WindowManager}'s.
./base/services/java/com/android/server/PowerManagerService.java:??????????????????? && !"KeyInputQueue".equals(tag))) {
./base/services/java/com/android/server/WindowManagerService.java:import com.android.server.KeyInputQueue.QueuedEvent;
./base/services/java/com/android/server/WindowManagerService.java:??????? implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
./base/services/java/com/android/server/WindowManagerService.java:??????? return KeyInputQueue.getSwitchState(sw);
./base/services/java/com/android/server/WindowManagerService.java:??????? return KeyInputQueue.getSwitchState(devid, sw);
./base/services/java/com/android/server/WindowManagerService.java:??????? return KeyInputQueue.hasKeys(keycodes, keyExists);
./base/services/java/com/android/server/WindowManagerService.java:??? private class KeyQ extends KeyInputQueue
./base/services/java/com/android/server/WindowManagerService.java:??????????? implements KeyInputQueue.FilterCallback {
./base/services/java/com/android/server/InputDevice.java:??? // For use by KeyInputQueue for keeping track of the current touch
./base/services/java/com/android/server/InputDevice.java:??????????? if (KeyInputQueue.BAD_TOUCH_HACK) {
./base/services/java/com/android/server/InputDevice.java:??????????????????? Slog.i("KeyInputQueue", "Updating: " + currentMove);
./base/services/java/com/android/server/InputDevice.java:??????????????????? Slog.i("KeyInputQueue", "Updating: " + currentMove);

?

10.從上面的查找結果得知,會在WindowManagerService.java中有一個KeyQ類繼承KeyInputQueue類,再在這個文件中查找KeyQ類在哪里定義并實例化的,找到在其構造函數里實例化的。

?

?? private WindowManagerService(Context context, PowerManagerService pm,?
??????????? boolean haveInputMethods) {
??????? if (MEASURE_LATENCY) {
??????????? lt = new LatencyTimer(100, 1000);
??????? }

?????????? ....

??? mQueue = new KeyQ();?

??????? mInputThread = new InputDispatcherThread();

??????? PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
???????? ...

}

?

至此,基本上把Input event的Framework的流程全部走完了。WindowManagerService是屬于System server進程里面起的一個Service.一開機就會運行,當然其構造函數一開機就能會運行。

?

?

至此,整個流程如下:

?????????????? WindowManagerService

???????????????????????????? |

???????????????????????????? |

??????????????????????????? //

???????????????????????? KeyQ

???????????????????????????? |

???????????????????????????? |

??????????????????????????? //

??????????????????????? KeyInputQueue

???????????????????????????? |

???????????????????????????? |

??????????????????????????? //

??????????????????????? EventHub

???????????????????????????? |

???????????????????????????? |

??????????????????????????? //

???????????????????????? Kernel device (/dev/input)

?

后續的文章將介紹/dev/input在Kernel中的實現。


轉載于:https://my.oschina.net/kings0527/blog/520703

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的Android 中input event的分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。