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

Android系统层的input设备解析

2012-08-24 16:08 351 查看
前言:这篇从2011年写到2012年,呵呵,2012来临了,祝大家新年快乐,心想事成。

上一篇从linux内核角度分析input驱动,那么android怎么获取input信息呢?本文重点讨论这个话题。

在Java层,处理input类型消息在InputManager.java文件里,当然首先要找到源头,即InputManager类由谁来创建?在WindowManagerService.java这个界面窗口管理服务文件里。

WindowManagerService类的构造函数:

private WindowManagerService(Context context, PowerManagerService pm,

boolean haveInputMethods, boolean showBootMsgs) {

…..

mInputManager = new InputManager(context, this);

…..

mInputManager.start();

.….

}

…..省掉与InputManager无关的部分,创建InputManager对象,然后调用其start方法来监控input事件。

进入InputManager.java文件,先看看它的构造函数,之后分析start方法。

public InputManager(Context context, WindowManagerService windowManagerService) {

this.mContext = context;

this.mWindowManagerService = windowManagerService;

this.mCallbacks = new Callbacks();//创建回调对象

Looper looper = windowManagerService.mH.getLooper();//创建looper

Slog.i(TAG, "Initializing input manager");

nativeInit(mContext, mCallbacks, looper.getQueue());//调用本地方法nativeInit来//进行C++层的初始化操作

// Add ourself to the Watchdog monitors.

Watchdog.getInstance().addMonitor(this);//watchdog监视器

}

在这里,重点关注nativeInit。进入C++层,在com_android_server_InputManager.cpp文件:

static JNINativeMethod gInputManagerMethods[] = {

/* name, signature, funcPtr */

{ "nativeInit", "(Landroid/content/Context;"

"Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",

(void*) android_server_InputManager_nativeInit },// nativeInit与java层的关//联

{ "nativeStart", "()V",

(void*) android_server_InputManager_nativeStart },// nativeStart与java层的关联

………..

}

在这里,分析android_server_InputManager_nativeInit函数.

static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,

jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {

// gNativeInputManager为空,便创建对象.

if (gNativeInputManager == NULL) {

sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);// sp<Looper>: Looper类强指针

gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);//创建NativeInputManager对象

} else {

LOGE("Input manager already initialized.");

jniThrowRuntimeException(env, "Input manager already initialized.");

}

}

通过android_server_InputManager_nativeInit函数完成Looper和NativeInputManager初始化。

重点关注NativeInputManager类构造函数:

NativeInputManager::NativeInputManager(jobject contextObj,

jobject callbacksObj, const sp<Looper>& looper) :

mLooper(looper) {

JNIEnv* env = jniEnv();

mContextObj = env->NewGlobalRef(contextObj);

mCallbacksObj = env->NewGlobalRef(callbacksObj);

{

AutoMutex _l(mLock);

mLocked.displayWidth = -1;

mLocked.displayHeight = -1;

mLocked.displayExternalWidth = -1;

mLocked.displayExternalHeight = -1;

mLocked.displayOrientation = DISPLAY_ORIENTATION_0;

mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;

mLocked.pointerSpeed = 0;

mLocked.pointerGesturesEnabled = true;

mLocked.showTouches = false;

}

sp<EventHub> eventHub = new EventHub();//new一个EventHub对象

mInputManager = new InputManager(eventHub, this, this);//创建InputManager对象

}

这个函数创建一个EventHub对象,然后把它作为参数来创建InputManager对象。特别注意,InputManager是在C++里,具体在InputManager.cpp里。EventHub类在EventHub.cpp里,这个类和input事件获取有关。

首先是去InputManager.cpp文件,下面是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();

}

它创建了InputDispatcher对象,同时也创建了InputReader对象。并分别暂存于mDispatcher和mReader变量中。注意eventHub和mDispatcher都作为参数创建InputReader对象。后面还用initialize来初始化。下面是initialize函数的定义:

void InputManager::initialize() {

mReaderThread = new InputReaderThread(mReader);

mDispatcherThread = new InputDispatcherThread(mDispatcher);

}

它创建两个线程,一个是InputReaderThread线程,负责input事件的获取;另一个是InputDispatcherThread线程,负责input消息的发送。

先回头解决在开始InputManager.java的mInputManager.start()这个start方法。看究竟怎么启动。

public void start() {

Slog.i(TAG, "Starting input manager");

nativeStart();//调用本地方法nativeStart

…..

}

重点关注nativeStart。进入C++层,在com_android_server_InputManager.cpp文件:

static JNINativeMethod gInputManagerMethods[] = {

/* name, signature, funcPtr */

{ "nativeInit", "(Landroid/content/Context;"

"Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",

(void*) android_server_InputManager_nativeInit },// nativeInit与java层的关//联

{ "nativeStart", "()V",

(void*) android_server_InputManager_nativeStart },// nativeStart与java层的关联

………..

}

关注android_server_InputManager_nativeStart函数:

static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {

if (checkInputManagerUnitialized(env)) {

return;

}

status_t result = gNativeInputManager->getInputManager()->start();

if (result) {

jniThrowRuntimeException(env, "Input manager could not be started.");

}

先看getInputManager:

inline sp<InputManager> getInputManager() const { return mInputManager; }

getInputManager是InputManager类,它的start方法继承InputManager类的方法(注意是InputManager.cpp的InputManager类)。

status_t InputManager::start() {

status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

if (result) {

LOGE("Could not start InputDispatcher thread due to error %d.", result);

return result;

}

result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

if (result) {

LOGE("Could not start InputReader thread due to error %d.", result);

mDispatcherThread->requestExit();

return result;

}

return OK;

}

它主要是启动InputDispatcherThread和InputReaderThread这两个线程。前面提到了创建这两个线程。也创建了InputDispatcher和InputReader对象。下面就这两个对象做分解.

下面是class InputDispatcherThread : public Thread {

public:

explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);

~InputDispatcherThread();

private:

virtual bool threadLoop();

sp<InputDispatcherInterface> mDispatcher;

};

由于它是Thread子类,于是继承它的run方法,进入run方法后会调用threadLoop(),在Thread类中它是虚函数,得由子类来复写,如下所示:

bool InputDispatcherThread::threadLoop() {

mDispatcher->dispatchOnce();

return true;

}

启动mDispatcher->dispatchOnce();

void InputDispatcher::dispatchOnce() {

nsecs_t nextWakeupTime = LONG_LONG_MAX;

{ // acquire lock

AutoMutex _l(mLock);

dispatchOnceInnerLocked(&nextWakeupTime);

if (runCommandsLockedInterruptible()) {

nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately

}

} // release lock

// Wait for callback or timeout or wake. (make sure we round up, not down)

nsecs_t currentTime = now();

int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);

mLooper->pollOnce(timeoutMillis);

}

函数功能:dispatchOnceInnerLocked函数处理input输入消息,mLooper->pollOnce是等待下一次输入事件。

mLooper->pollOnce(timeoutMillis):

这个请看Looper.cpp文件中的Looper::pollOnce()函数。Looper里主要通过linux管道方式实现进程间通信,通过epoll机制实现外界事件请求作出响应。

接着,来分析InputReaderThread的启动。

class InputReaderThread : public Thread {

public:

InputReaderThread(const sp<InputReaderInterface>& reader);

virtual ~InputReaderThread();

private:

sp<InputReaderInterface> mReader;

virtual bool threadLoop();//loop

};

在这里直接到InputReader.cpp文件

bool InputReaderThread::threadLoop() {

mReader->loopOnce();

return true;

}

往下走,

void InputReader::loopOnce() {

int32_t timeoutMillis;

{ // acquire lock

…….

size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

……

}

这个函数主要通过EventHub的getEvents方法来获取input事件。

接下来进入到EventHub.cpp文件。一睹getEvents方法。

bool EventHub::getEvent(RawEvent* outEvent)

{

outEvent->deviceId = 0;

outEvent->type = 0;

outEvent->scanCode = 0;

outEvent->keyCode = 0;

outEvent->flags = 0;

outEvent->value = 0;

outEvent->when = 0;

// Note that we only allow one caller to getEvent(), so don't need

// to do locking here... only when adding/removing devices.

if (!mOpened) {

mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;

mOpened = true;

mNeedToSendFinishedDeviceScan = true;

}

for (;;) {

// Report any devices that had last been added/removed.

if (mClosingDevices != NULL) {

device_t* device = mClosingDevices;

LOGV("Reporting device closed: id=0x%x, name=%s\n",

device->id, device->path.string());

mClosingDevices = device->next;

if (device->id == mFirstKeyboardId) {

outEvent->deviceId = 0;

} else {

outEvent->deviceId = device->id;

}

outEvent->type = DEVICE_REMOVED;

outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

delete device;

mNeedToSendFinishedDeviceScan = true;

return true;

}

if (mOpeningDevices != NULL) {

device_t* device = mOpeningDevices;

LOGV("Reporting device opened: id=0x%x, name=%s\n",

device->id, device->path.string());

mOpeningDevices = device->next;

if (device->id == mFirstKeyboardId) {

outEvent->deviceId = 0;

} else {

outEvent->deviceId = device->id;

}

outEvent->type = DEVICE_ADDED;

outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

mNeedToSendFinishedDeviceScan = true;

return true;

}

if (mNeedToSendFinishedDeviceScan) {

mNeedToSendFinishedDeviceScan = false;

outEvent->type = FINISHED_DEVICE_SCAN;

outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

return true;

}

// Grab the next input event.

for (;;) {

// Consume buffered input events, if any.

if (mInputBufferIndex < mInputBufferCount) {

const struct input_event& iev = mInputBufferData[mInputBufferIndex++];

const device_t* device = mDevices[mInputDeviceIndex];

LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),

(int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);

if (device->id == mFirstKeyboardId) {

outEvent->deviceId = 0;

} else {

outEvent->deviceId = device->id;

}

outEvent->type = iev.type;

outEvent->scanCode = iev.code;

if (iev.type == EV_KEY) {

status_t err = device->layoutMap->map(iev.code,

& outEvent->keyCode, & outEvent->flags);

LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",

iev.code, outEvent->keyCode, outEvent->flags, err);

if (err != 0) {

outEvent->keyCode = AKEYCODE_UNKNOWN;

outEvent->flags = 0;

}

} else {

outEvent->keyCode = iev.code;

}

outEvent->value = iev.value;

// Use an event timestamp in the same timebase as

// java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()

// as expected by the rest of the system.

outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

return true;

}

// Finish reading all events from devices identified in previous poll().

// This code assumes that mInputDeviceIndex is initially 0 and that the

// revents member of pollfd is initialized to 0 when the device is first added.

// Since mFDs[0] is used for inotify, we process regular events starting at index 1.

mInputDeviceIndex += 1;

if (mInputDeviceIndex >= mFDCount) {

break;

}

const struct pollfd& pfd = mFDs[mInputDeviceIndex];

if (pfd.revents & POLLIN) {

int32_t readSize = read(pfd.fd, mInputBufferData,

sizeof(struct input_event) * INPUT_BUFFER_SIZE);

if (readSize < 0) {

if (errno != EAGAIN && errno != EINTR) {

LOGW("could not get event (errno=%d)", errno);

}

} else if ((readSize % sizeof(struct input_event)) != 0) {

LOGE("could not get event (wrong size: %d)", readSize);

} else {

mInputBufferCount = readSize / sizeof(struct input_event);

mInputBufferIndex = 0;

}

}

}

#if HAVE_INOTIFY

// readNotify() will modify mFDs and mFDCount, so this must be done after

// processing all other events.

if(mFDs[0].revents & POLLIN) {

readNotify(mFDs[0].fd);

mFDs[0].revents = 0;

continue; // report added or removed devices immediately

}

#endif

mInputDeviceIndex = 0;

// Poll for events. Mind the wake lock dance!

// We hold a wake lock at all times except during poll(). This works due to some

// subtle choreography. When a device driver has pending (unread) events, it acquires

// a kernel wake lock. However, once the last pending event has been read, the device

// driver will release the kernel wake lock. To prevent the system from going to sleep

// when this happens, the EventHub holds onto its own user wake lock while the client

// is processing events. Thus the system can only sleep if there are no events

// pending or currently being processed.

release_wake_lock(WAKE_LOCK_ID);

int pollResult = poll(mFDs, mFDCount, -1);

acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);

if (pollResult <= 0) {

if (errno != EINTR) {

LOGW("poll failed (errno=%d)\n", errno);

usleep(100000);

}

}

}

}

函数功能:如果是第一次进入到这个函数中时,并且成员变量mOpened的值为false,于是就会调用openPlatformInput函数来打开系统输入设备。打开了这些输入设备文件后,就可以对这些输入设备进行是监控了。如果不是第一次进入到这个函数,那么就会分析当前有没有input事件发生,如果有,就返回这个事件,否则就会进入等待状态,等待下一次input事件的发生。这个函数很关键,上次分析input内核驱动,通过这个函数读取(打开设备文件形式)由input驱动传送过来的事件信息。

这一节分解到这里,下回再分解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: