前言:这篇从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
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
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
mInputManager = new InputManager(eventHub, this, this);//创建InputManager对象
}
这个函数创建一个EventHub对象,然后把它作为参数来创建InputManager对象。特别注意,InputManager是在C++里,具体在InputManager.cpp里。EventHub类在EventHub.cpp里,这个类和input事件获取有关。
首先是去InputManager.cpp文件,下面是InputManager类的构造函数:
InputManager::InputManager(
const sp
const sp
const sp
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
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
~InputDispatcherThread();
private:
virtual bool threadLoop();
sp
};
由于它是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
virtual ~InputReaderThread();
private:
sp
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驱动传送过来的事件信息。
这一节分解到这里,下回再分解。