您的位置:首页 > 移动开发 > Android开发

一步步追踪android输入事件(一)

2016-06-18 17:05 363 查看

零.写在最前

第一次尝试阅读android输入系统的代码,免不了理解错误,如有错误,欢迎指正。

一.提出问题

android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。对android系统而言,是谁在读这些设备文件节点?读到以后又是怎么把它发送给view的?

二.猜测与验证

事件是一种看不到的东西,在android下,看不见的东西一般交给service来处理,系统service在系统启动的时候注册。android的输入事件的管理,应该是在系统启动的时候,注册成为系统的服务的。系统服务的注册在framworks/base/services/java/com/android/server/SystemServer.java中,使用addService来注册,在这个文件中搜索Input,很容易就发现有个InputManagerService类,从类名上看应该是输入事件管理服务类的意思,和我猜测的差不多。进去这个类看看:

/*
* Wraps the C++ InputManager and provides its callbacks.
*/
public class InputManagerService extends IInputManager.Stub
implements Watchdog.Monitor {
看一个类先看它的注释,注释中说:包装C++的inputManager 并且提供它的回调。再看这个类是继承于IInputManager.Stub的,就大概知道它实现来远程系统调用的接口,其实所有的服务类都会继承于IInputManager.Stub,因为stub继承自binder类,而binder是android用于进程间通信的。stub类的声明如下:

/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.hardware.input.IInputManager
{
回过头来,既然输入系统以服务的形式进行管理,那先看看把InputManagerService注册位服务的代码,就在SystemServer.java中往下索InputManagerService,就会发现:

Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context);

Slog.i(TAG, "Window Manager");
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);

mActivityManagerService.setWindowManager(wm);

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();

这里首先创建了一个InputManagerService的实例,然后把它传给了一个main函数,这里是个疑问,不知道是干什么的,暂时不管,然后就是重点了,可以看到调用了

ServiceManager的addService方法注册一个service.这里注册的当然是InputManagerService。注册后,给inputManager设置了一些回调函数,然后,就调用了start函数。

那当然显示从InputManagerSercie的构造函数入手了,毕竟构造函数最先杯调用。start方法暂时放置,这个方法应该很重要,从名字可以猜测它是启动事件输入框架的。

三.InputManagerService的构造函数

<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;"> public InputManagerService(Context context) {</span>
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
+ mUseDevInputEventForAudioJack);
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

LocalServices.addService(InputManagerInternal.class, new LocalService());
}


它只有一个构造函数,看起来也不是很难。首先创建了一个Handler,其次去读mUseDevInputEventForAudioJack变量的值,这个值应该实在某个地方配置好的,暂且

不管它,然后就注册了一个handler,那看下这个handler是做什么的:


</pre><pre code_snippet_id="1720818" snippet_file_name="blog_20160618_11_1111284" name="code" class="java">        public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DELIVER_INPUT_DEVICES_CHANGED:
deliverInputDevicesChanged((InputDevice[])msg.obj);
break;
case MSG_SWITCH_KEYBOARD_LAYOUT:
handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
break;
case MSG_RELOAD_KEYBOARD_LAYOUTS:
reloadKeyboardLayouts();
break;
case MSG_UPDATE_KEYBOARD_LAYOUTS:
updateKeyboardLayouts();
break;
case MSG_RELOAD_DEVICE_ALIASES:
reloadDeviceAliases();
break;
}



具体看不懂,表面上看好像是处理输入时间变动之类的东西,暂且不管,先看它的nativeInit方法,这份方法应该比较重要吧,要不然下一如看什么地方呀?先做好思想准备,nativeInit这个函数应该比较重要,可是这个方法在哪里呢?应该在一个向InputManagerService中注册方法的本地.cpp文件中,在android的framwork下搜索:

find -name *InputManagerService* 发现

base/services/core/jni/com_android_server_input_InputManagerService.cpp

进去搜nativeInit果然存在:

static jlong nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}

NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}


这里面创建了一个NativeInputManager类的实例,并且调用了它的incStrong方法。NativeInputManager应该是InputManager的C++部分,Android系统的代码是很规范的,从名字可以猜出个一二。那先看看NativeInputManager的构造函数吧。

三.NativeInputManager的构造函数与incStrong方法。

NativeInputManager同样定义在base/services/core/jni/com_android_server_input_InputManagerService.cpp中。

NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
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<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}

它的构造函数有个代码块,大概是做一些初始化的工作,然后创建了两个看着很重要的东西,eventHub和mInputManager,可以看到eventHub作为参数传给了

InputManager的构造函数,所以,这里着重看一下InputManager的构造函数。

四.InputManager的构造函数

InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
这里又创建了InputReader, InputDispatcher两个类的实例,然后调用initialize();用来初始化。代码到这里就越来越复杂了,头绪也越来越多,但是不能忘记我们的目标,也就是我们搞懂一开始提出的两个疑问,我们先想想第一个问题:“是谁在读/dev/input/下的文件节点?”,说到读,那这的InputReader是不是就是读的呢?从名字上
来看好像是的。所以,这里,我们还是要牢牢的抓住主干,先搞明白是谁在读/dev/input/下的文件节点,然后再思考其他的部分。那么,这里暂时对InputDisPatcher和initialize置之不理,着重看下InputReader到底做了什么?

五.InputReader的构造函数。

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);

{ // acquire lock
AutoMutex _l(mLock);

refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
这里面就调用了两个函数,首先看第一个函数:refreshConfigurationLocked(0);

void InputReader::refreshConfigurationLocked(uint32_t changes) {
mPolicy->getReaderConfiguration(&mConfig);
mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);

if (changes) {
ALOGI("Reconfiguring input devices.  changes=0x%08x", changes);
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
mEventHub->requestReopenDevices();
} else {
for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
device->configure(now, &mConfig, changes);
}
}
}
}
这个函数也是看的一脸懵逼,可以看到出现了InputDevice 这个类,从名字上看这不就是输入设备吗?然后调用了这个类的configure函数,这不就是配置一些参数吗?看来好像没错,这个类就是对/dev/input/下设备文件类进行读写的类。再看第二个函数:

void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;

for (size_t i = 0; i < mDevices.size(); i++) {
InputDevice* device = mDevices.valueAt(i);
mGlobalMetaState |= device->getMetaState();
}
}


可以看到也是操作这个InputDevice。这个类到底是何方圣神,它到底是不是代表/dev/input/那些设备文件节点?

六.InputDevice

InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) :
mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber),
mIdentifier(identifier), mClasses(classes),
mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
}

InputDevice的构造函数是空的,那看看它有什么方法:

1.int32_t
InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc)--获得状态?

2.boolInputDevice::markSupportedKeyCodes(uint32_t
sourceMask, size_t numCodes,

const int32_t* keyCodes, uint8_t* outFlags)                         --支持的按键码?

3void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo)            --获得设备的信息?


4.boolisKeyPressed(int32_t code) {
return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
}                                                                           --判断按键是否按下吧?

。。。

可以看到它确实与/dev/input/下的设备文件节点密切相关。但并没有直接操作,而是借助getEventHub返回的EventHubInterface进行操作的。

七.EventHubInterface和EventHub

EventHubInterface是个虚函数,它的实现类是EventHub.

那我们想一下,要怎么读/dev/input/设备文件节点,怎么读呢?一般是多路复用吧?select,poll,epoll等,那我们在EventHub中搜搜看。

搜索的结果是select没找到,poll没出现,但是出现了epoll.搜索epoll_ctl等试试:

if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
重要的三个函数都出现了,所以这里应该就是对/dev/input读写的地方了。
具体的代码就不继续分析了,确实看的很累,这里是不是就找到了第一个问题的答案了呢?别忘了第一个问题:“是谁在读这些设备文件节点?”。

是的,就是这个EventHub类。

仔细回想一下,现在只是根据猜测加阅读,找到了读写/dev/input/设备文件节点的位置。可以想象一下,如果是自己写这部分代码,应该要一直监听这些输入设备吧,

应该有一个线程,一直循环监听这些输入设备,有事件的时候就去处理,没有事件的时候就睡眠,等待事件的到来。那么,这部分的代码是怎么样的呢?这次的分析知识找到了

一开始猜想的第一个答案,第二个答案还没有找到。要找到第二个答案,就需要更加细致的分析前面的流程,找到更多的内容,尤其要找到一个循环监听输入设备的线程的地方,

我相信它肯定存在,至于它是在什么地方创建,怎么调用的,下一节继续分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: