1、kernel中同按键相关代码
(这里以xxx代替,是因为针对不同的硬件,需要的Linux kernel不同)
当然从核心板原理图到kernel是属于驱动范畴,不讨论。
2、framework针对键盘事件的处理
Step 1
在SystemServer.java
- class
ServerThread extends Thread {
-
//省略。。
-
public void run() {
-
// Create a handler thread just for the window manager to enjoy.
-
HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
-
wmHandlerThread.start();
-
Handler wmHandler = new Handler(wmHandlerThread.getLooper());
-
//此处省略5k字。。
-
Slog.i(TAG, "Input Manager");
-
inputManager = new InputManagerService(context, wmHandler);
-
}
- }
可以看到,在系统启动的时候,会首先创建一个系统级别的Handler线程wmHandlerThread用于处理键盘消息(仅说明键盘消息)。然后在创建输入管理服务
Step 2
在往下走到
- public
InputManagerService(Context context, Handler handler) {
-
this.mContext = context;
-
this.mHandler = new InputManagerHandler(handler.getLooper());
-
mUseDevInputEventForAudi oJack =
-
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudio Jack);
-
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudi oJack="
-
+ mUseDevInputEventForAudi oJack);
-
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
- }
这里做了重要的两件事情,第一:将SystemServer级别的Handler赋值给
Step 3
com_android_server_ InputManagerService.cpp
- static jint nativeInit(JNIEnv*
env, jclass clazz,
-
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
-
sp<</span>MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
-
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
-
messageQueue->getLooper());
-
im->incStrong(serviceObj);
-
return reinterpret_cast<</span>jint>(im);
- }
这里nativeInit直接调用了
Step 4
- NativeInputManager::NativeInputManager(jobject contextObj,
-
jobject serviceObj, const sp<</span>Looper>& looper) :
-
mLooper(looper) {
-
JNIEnv* env = jniEnv();
-
mContextObj = env->NewGlobalRef(contextObj);
-
mServiceObj = env->NewGlobalRef(serviceObj);
-
{
-
AutoMutex _l(mLock);
-
mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
-
mLocked.pointerSpeed = 0;
-
mLocked.pointerGesturesEnabled = true;
-
mLocked.showTouches = false;
-
}
-
sp<</span>EventHub> eventHub = new EventHub();
-
mInputManager = new InputManager(eventHub, this, this);
- }
这里需要特别注意最后两行代码。第一:创建了
Step 5
接下来继续看看InputManager的构造函数。
- InputManager::InputManager(
-
const sp<</span>EventHubInterface>& eventHub,
-
const sp<</span>InputReaderPolicyInterfa ce>& readerPolicy,
-
const sp<</span>InputDispatcherPolicyInt erface>& dispatcherPolicy) {
-
mDispatcher = new InputDispatcher(dispatcherPolicy);
-
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
-
initialize(); - }
- void InputManager::initialize()
{
-
mReaderThread = new InputReaderThread(mReader);
-
mDispatcherThread = new InputDispatcherThread(mDispatcher);
- }
创建了InputDispatcher
到这里,相关的组件都已经被创建了;
Step 6
接下来看看他们是如何运行起来的。
在systemServer.java中创建inputManager之后。将InputManagerServer进行注册,并运行start()
- ServiceManager.addService(Context.INPUT_SERVICE,
inputManager);
-
inputManager.start();
-
//InputManager的start函数:
-
public void start() {
-
Slog.i(TAG, "Starting input manager");
-
nativeStart(mPtr);
-
//省略。。
- }
调用nativeStart继续往下走。顺带说一下,这里的参数mPtr是指向native inputmanager service对象的,在InputManagerService构造函数中由nativeInit赋值。
Step 7
接下来又到了com_android_server_ InputManagerService.cpp中。
- static void nativeStart(JNIEnv*
env, jclass clazz, jint ptr) {
-
NativeInputManager* im = reinterpret_cast<</span>NativeInputManager*>(ptr);
-
status_t result = im->getInputManager()->start();
-
if (result) {
-
jniThrowRuntimeException (env, "Input manager could not be started.");
-
}
- }
这里的im就是inputManager并且用到了上面传下来的mPtr来重新构建。
Step 8
继续往下则会调用到InputManager.cpp
- status_t InputManager::start()
{
-
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
-
if (result) {
-
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
-
return result;
-
}
-
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
-
if (result) {
-
ALOGE("Could not start InputReader thread due to error %d.", result);
-
mDispatcherThread->requestExit();
-
return result;
-
}
-
return OK;
- }
这个函数主要就是分别启动一个InputDispatcherThread线程和一个InputReaderThread线程来读取和分发键
Step 9
在下来继续看loopOnce()这个函数。
- void InputReader::loopOnce()
{
-
//......
-
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
-
//......
-
if (count) {
-
processEventsLocked(mEventBuffer, count);
-
}
-
//......
-
// Flush queued events out to the listener.
-
// This must happen outside of the lock because the listener could potentially call
-
// back into the InputReader's methods, such as getScanCodeState, or become blocked
-
// on another thread similarly waiting to acquire the InputReader lock thereby
-
// resulting in a deadlock. This situation is actually quite plausible because the
-
// listener is actually the input dispatcher, which calls into the window manager,
-
// which occasionally calls into the input reader.
-
mQueuedListener->flush();
- }
这里面需要注意像神一样的函数
- /*
- *
Wait for events to become available and returns them.
- *
After returning, the EventHub holds onto a wake lock until the next call to getEvent.
- *
This ensures that the device will not go to sleep while the event is being processed.
- *
If the device needs to remain awake longer than that, then the caller is responsible
- *
for taking care of it (say, by poking the power manager user activity timer).
- *
- *
The timeout is advisory only. If the device is asleep, it will not wake just to
- *
service the timeout.
- *
- *
Returns the number of events obtained, or 0 if the timeout expired.
- */
- virtual size_t getEvents(int
timeoutMillis, RawEvent* buffer, size_t bufferSize)
函数原型!
在成功获取input Event之后,就会用到
然后在调用到
- processEventsForDeviceLo
cked(deviceId, rawEvent, batchSize);
最后在
- void InputDevice::process(const
RawEvent* rawEvents, size_t count)
我就在想:问什么不直接到process函数呢?其实我觉得这里体现了设计模式中的单一职责原则;这种设计可以有效的控制函数粒度(有个类粒度,这里自创函数粒度)的大小,函数承担的职责越多其复用的可能性就越小,并且当期中某一个职责发生变化,可能会影响其他职责的运作!
Step 10
接下来继续看
- void InputDevice::process(const
RawEvent* rawEvents, size_t count) {
-
//。。。。
-
InputMapper* mapper = mMappers[i];
-
mapper->process(rawEvent);
- }
走到这里才算是真真正正的知道了有按键发生了,调用
请留意
- InputDevice*
InputReader::createDeviceLocked(int32_t deviceId,
- const
InputDeviceIdentifier& identifier, uint32_t classes)
函数中的片段:
- if
(keyboardSource != 0) {
-
device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
- }
这里Event Type有必要提一下,以下是一些常用的Event。在kernel/Documentation/input/event-codes.txt中有详细的描述。
* EV_SYN:
- Used as markers to separate events. Events may be separated in time or in space, such as with the multitouch protocol.
* EV_KEY:
- Used to describe state changes of keyboards, buttons, or other key-like devices.
* EV_REL:
- Used to describe relative axis value changes, e.g. moving the mouse 5 units to the left.
* EV_ABS:
- Used to describe absolute axis value changes, e.g. describing the coordinates of a touch on a touchscreen.
* EV_MSC:
- Used to describe miscellaneous input data that do not fit into other types.
* EV_SW:
-
Used to describe binary state input switches.
Step 11
- 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;
-
}
-
}
- }
在这里,先判断isKeyboardOrGamepadKey(scanCode),然后在用getEventHub()->mapKey()检测
Step 12
- void KeyboardInputMapper::processKey(nsecs_t when,
bool down, int32_t keyCode,
-
int32_t scanCode, uint32_t policyFlags) {
-
//忽略到所有的。。只看最后两行。。
-
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
-
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
-
getListener()->notifyKey(&args);
- }
不用多解释了,直接notifyKey了。。但需要注意,这里的notifyKey
还记不记得在void InputReader::loopOnce()这个函数的最后一行代码,其实质是在这个函数中通知上层有按键事件发生。
这个flush()很明显,notify了之后,就delete,不存在了。问什么不是在getListener()->notifyKey(&args);的时候就真正的notify?我觉得可以做如下角度予以考虑:
第一:线程是最小的执行单位;因此每当inputThread.start()的时候,如果不flush,回造成数据混乱。
第二:flush操作是必须的,同时在loopOnce的最后操作也是最恰当的。其实这里的Listener也就是充当了一个事件分发者的角色。
这说明,到这里已经完全识别了按键了,并按照自己的键盘映射映射了一个值保存在args中,notifyKey给上层应用了。。
附加:
Step 12
其实针对BACK HOME MENU这三个按键来说,其实质就是TouchScreen;因此在inputReader.cpp中获取Touch映射是在函数bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags)
首先检测不是多点Touch。然后使用const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit( int32_t x, int32_t y)依据坐标值查找出Touch的映射值。
Step 13