初始化
在SystemServer中实例化InputManagerService,注册WMS.callback,之后就启动InputManagerService。
sequenceDiagram
SystemServer->> InputManagerService: new InputManagerService
SystemServer->>WindowManagerService: new WindowManagerService with IMS
ActivityManagerService->>WindowManagerService:setWindowManager
InputManagerService->>WindowManagerService:setWindowManagerCallbacks
WindowManagerService->>InputManagerService:getInputMonitor
InputManagerService->>InputManagerService:start
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
sequenceDiagram
InputManagerService->> NativeInputManager: nativeInit
NativeInputManager->>InputDispatcher:new
NativeInputManager->>EventHub:new
NativeInputManager->>InputReader:new
InputReader->>InputReader:loopOnce
InputReader->>EventHub:getEvents
EventHub->>EventHub:epoll /dev/input/fd
InputReader->>KeyboardInputMapper:process
InputReader->>InputDispatcher:notifyKey
InputDispatcher->>NativeInputManager:interceptKeyBeforeQueueing
NativeInputManager->>InputManagerService:interceptKeyBeforeQueueing
InputManagerService->>InputMonitor:interceptKeyBeforeQueueing
InputMonitor->>PhoneWindowManager:interceptKeyBeforeQueueing
下面看一下InputManagerService的初始化流程:
public InputManagerService(Context context) {
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());//初始化一个handler。究竟什么作用之后分析。
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//初始化native层。
LocalServices.addService(InputManagerInternal.class, new LocalService());//将service注册到系统当中,这个在此不做分析。
}
看一下native层做了什么东西
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());//这里初始化native层的一个manager
}
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
sp<EventHub> eventHub = new EventHub();//初始化一个eventhub,用来接收event
mInputManager = new InputManager(eventHub, this, this);//将上面实例化的eventhub注册到manager中,并且注册listener,即NativeInputManager,用来与framework层进行通讯。
}
InputManager这个类很简单,只是开启两个线程去检测event事件,以及dispatch这些event。
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);//初始化event分发者
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);//初始化事件的读取者。
initialize();
}
//开机新的线程去检测这些事件
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
至此初始化流程基本结束。下面看一下是如何运行起来的呢?
在systemserver里面有inputManager.start();
看一下干了啥
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
}
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();//启动InputManager
}
//启动这两个thread
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
return OK;
}
之后就是监听按键,处理按键,分发按键的过程了。
事件的获取
在调用nativeStart时InputReader的启动
frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
void InputReader::loopOnce() {
//从EventHub中获取event
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
if (count) {//
processEventsLocked(mEventBuffer, count);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);//通知framework
}
mQueuedListener->flush();
}
事件的接收比较简单。下面介绍一下event是如何处理的以及如何传给framework层处理的。
事件的处理
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);//处理event
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
InputDevice* device = mDevices.valueAt(deviceIndex);//查找对应的device
device->process(rawEvents, count);//调用device的process函数去处理event
}
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
} else {
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
} else {
for (size_t i = 0; i < numMappers; i++) {//查找对应的mapper去处理这个按键信息
InputMapper* mapper = mMappers[i];
mapper->process(rawEvent);
}
}
}
}
这里以KeyboardInputMapper
为例:
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {//处理keyevent
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
>==processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);==<
}
break;
}
case EV_MSC:
case EV_SYN:
}
}
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
//获取scanCode
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
&keyCode, &keyMetaState, &policyFlags)) {
keyCode = AKEYCODE_UNKNOWN;
keyMetaState = mMetaState;
policyFlags = 0;
}
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);//调用之前注册进来的listener
}
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();//在native_init的时候注册进来的listener
}
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
>==mQueuedListener = new QueuedInputListener(listener);==<//在native_init的时候注册进来的listener,InputDispatcher。
}
下面看一下InputDispatcher.
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
if (!validateKeyEvent(args->action)) {//判断按键是否有效
return;
}
uint32_t policyFlags = args->policyFlags;
int32_t flags = args->flags;
int32_t metaState = args->metaState;
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
if (policyFlags & POLICY_FLAG_FUNCTION) {
metaState |= AMETA_FUNCTION_ON;
}
policyFlags |= POLICY_FLAG_TRUSTED;
int32_t keyCode = args->keyCode;
if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
int32_t newKeyCode = AKEYCODE_UNKNOWN;
if (keyCode == AKEYCODE_DEL) {
newKeyCode = AKEYCODE_BACK;
} else if (keyCode == AKEYCODE_ENTER) {
newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
AutoMutex _l(mLock);
struct KeyReplacement replacement = {keyCode, args->deviceId};
mReplacedKeys.add(replacement, newKeyCode);
keyCode = newKeyCode;
metaState &= ~AMETA_META_ON;
}
} else if (args->action == AKEY_EVENT_ACTION_UP) {
// In order to maintain a consistent stream of up and down events, check to see if the key
// going up is one we've replaced in a down event and haven't yet replaced in an up event,
// even if the modifier was released between the down and the up events.
AutoMutex _l(mLock);
struct KeyReplacement replacement = {keyCode, args->deviceId};
ssize_t index = mReplacedKeys.indexOfKey(replacement);
if (index >= 0) {
keyCode = mReplacedKeys.valueAt(index);
mReplacedKeys.removeItemsAt(index);
metaState &= ~AMETA_META_ON;
}
}
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
>>==mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);==<<//调用policy,加入到framework层的event队列中
}
那么jni是如何将这些事件传递给framework的呢?下面看一下这个函数的具体实现
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
bool interactive = mInteractive.load();
if (interactive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
if ((policyFlags & POLICY_FLAG_TRUSTED)) {
nsecs_t when = keyEvent->getEventTime();
JNIEnv* env = jniEnv();
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
jint wmActions;
if (keyEventObj) {
//通过jni去调用framework中的函数interceptKeyBeforeQueueing
>>== wmActions = env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeQueueing,
keyEventObj, policyFlags);==<<
if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
wmActions = 0;
}
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
} else {
if (interactive) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
}
这样的话就把event抛给framework层了。之后就是framework层处理按键event了。
那么到目前为止就把keyevent的收取,分发以及传递给framework层的流程分析完了。当然里面有许多细节的问题没有详细介绍。比如scancode和keycode之间的映射关系等。之后再补充。
framework对按键的处理流程
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
还记得在SystemServer中创建InputManagerService吗?
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
mWindowManagerCallbacks = callbacks;
}
public InputMonitor getInputMonitor() {
return mInputMonitor;
}
final InputMonitor mInputMonitor = new InputMonitor(this);
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
final WindowManagerPolicy mPolicy = new PhoneWindowManager();
就是在InputManagerService中接收到的keyevent会传给wm中,再传递给PhoneWindowManager去处理。这个函数太长,这里就不再贴出来了,文件位置:frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
键值存放的地方:
frameworks/base/data/keyboards/Generic.kl
android 中键值间的映射关系。
在寻找android和Generic.kl之间的映射关系的时候,遇到c语言中的#
和##
的用法、
#
用于将参数转换为字符串
##
用于将宏两个参数链接在一起
看一下getKeyCodeByLabel
#include <android/keycodes.h>
static int32_t getKeyCodeByLabel(const char* label) {
return int32_t(lookupValueByLabel(label, KEYCODES));
}
static const char* lookupLabelByValue(int value, const InputEventLabel* list) {
while (list->literal) {
if (list->value == value) {
return list->literal;
}
list++;
}
return NULL;
}
struct InputEventLabel {
const char *literal;
int value;
};
static const InputEventLabel KEYCODES[] = {
DEFINE_KEYCODE(L),
}
#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key }
这里只是以字母L
来进行分析。
如果宏展开的话
static const InputEventLabel KEYCODES[] = {
{"L",AKEYCODE_L }
}
如果单纯这样看的话返回值肯定不会是int32_t,但是从log看返回的keycode确实是40。那是如何转换过来的呢?
其实一个很重要的信息被忘记了。看一下包含进来的头文件android/keycodes.h
在ndk中可以找到AKEYCODE_L
这个宏的定义AKEYCODE_L= 40
。记得如果添加一个特殊的键值得时候有可能需要在编译的ndk中添加键值的定义哦~~
键值的解析流程
if (device) {
// Check the key character map first.
sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm != NULL) {
ALOGD("LIYANG Check the key character map first");
if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {//第一次解析,如果解析失败就调用device的layout去解析
*outFlags = 0;
status = NO_ERROR;
}
}
// Check the key layout next.
if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {//调用device的layout去解析键值。
ALOGD("LIYANG Check the key layout next.");
if (!device->keyMap.keyLayoutMap->mapKey(
scanCode, usageCode, outKeycode, outFlags)) {
status = NO_ERROR;
}
}
例如:
点击enter键
01-04 06:27:03.802 1612 3545 D InputReader: liang scanCode: 28, usageCode: 0
01-04 06:27:03.802 1612 3545 D KeyCharacterMap: mapKey: scanCode=28, usageCode=0x00000000 ~ Failed.//第一次解析失败
01-04 06:27:03.802 1612 3545 D KeyCharacterMap: tryRemapKey: keyCode=66, metaState=0x00000000 ~ replacement keyCode=66, replacement metaState=0x00000000.//第二次解析成功,返回keycode 66
01-04 06:27:03.802 1612 3545 D InputReader: liyang scanCode 28, usageCode:0, keyCode:66
scancode和keycode的对应关系在Generic.kl中。
key 28 ENTER
之后再android/keycodes.h
中可以找到AKEYCODE_ENTER = 66
。之后就将keycode返回给framework了。
上面只是介绍了InputManagerService中的流程,在SystemServer中还有一个service与InputManagerService有关,那就是WindowManagerService。接下来在看一下WindowManagerService的初始化流程。
文件位置:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
public static WindowManagerService main(final Context context,
final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = new WindowManagerService(context, im,
haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
···
mInputManager = inputManager; // Must be before createDisplayContentLocked.
LocalServices.addService(WindowManagerPolicy.class, mPolicy);//PhoneWindowManager
mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
···
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
···
}
这里面多数就是读取配置,这里面省略掉了。
看一下PointerEventDispatcher的初始化做了什么东西。
我们看到初始化PointerEventDispatcher的时候,从ims中获取了一个monitor
先来看一下monitor是啥以及是什么作用
InputManagerService.java
public InputChannel monitorInput(String inputChannelName) {
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);//socket 0
inputChannels[0].dispose(); // don't need to retain the Java object reference
return inputChannels[1];
}
通过上面,可以看到,总共做了两件事:1.建立两个channel;2.将channel[0]注册进去
先看一下两个channel是如何建立的
public static InputChannel[] openInputChannelPair(String name) {
return nativeOpenInputChannelPair(name);
}
frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
···
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
···
}
frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
InputChannel::InputChannel(const String8& name, int fd) :
mName(name), mFd(fd) {
int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
}
这里主要是通过调用socketpair建立两个socket 名称为WindowManager(server),WindowManager(client),并且设置一些属性。
接下来就是将这两个channel注册进去。我们知道socket0 是server端。所以需要注册socket0.
下面是将socket0注册进去,这里需要注意的是,socket0的作用不是去接收键值,主要是为了处理framework中处理完按键之后的流程。
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
}
//因为传进来的是空,所以这里inputWindowHandle也是空
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
}
frameworks/base/core/jni/android_view_InputChannel.cpp
通过加载inputChanne这个类的方式获取到channel。
sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
NativeInputChannel* nativeInputChannel =
android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}
之后就是将上面获取到的channel也就是socket0注册到NativeInputManager中。
下面看一下是如何注册的
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
通过上面的学习知道这里面的dispatcher就是frameworks/native/services/inputflinger/InputDispatcher.cpp
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
//这里首先检查一下这个channel是否注册过,如果注册过就返回错误
if (getConnectionIndexLocked(inputChannel) >= 0) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
//如果之前没有注册过就放入到mConnectionsByFd中,然后加入到looper中。如过有ALOOPER_EVENT_INPUT事件进来的话就调用handleReceiveCallback
//这里要区分一下,此处的mLooper是在Dispatcher中重新new的对象。
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}