启动初始化
Android的键盘事件由InputManager监控。
先来看下InputManager是如何启动的。
Android在启动的时候,Zygote会启动SystemServer.java
目录:frameworks/base/services/java/com/android/server/SystemServer.java
class ServerThread {
……..
public voidinitAndLoop() {
…….
// Create a handler thread just for thewindow manager to enjoy.
HandlerThread wmHandlerThread = newHandlerThread("WindowManager");
wmHandlerThread.start();
Handler wmHandler = newHandler(wmHandlerThread.getLooper());
…….
inputManager = new InputManagerService(context, wmHandler);
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power, display, inputManager,
wmHandler, factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
……….
}
}
SystemServer在初始化的时候,会调用WindowManagerServer的main函数。同时,初始化InputManagerService并启动InputManagerService。在这里将wmHandler的句柄传给了InputManagerServer。
那么,在WindowManagerServer.main又做了什么?
frameworks/base/services/java/com/android/server/wm./WindowManagerServer.java
public static WindowManagerService main(final Context context,
final PowerManagerService pm, final DisplayManagerService dm,
final InputManagerService im, final Handler wmHandler,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
wmHandler.runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = newWindowManagerService(context, pm, dm, im,
haveInputMethods,showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
这里main,实例化了一个WindowManagerServer。这里传入的inputmanager是怎么用的呢?InputManager在4.4.4中,在WindowmanagerServer中InputManager提供状态以及UI的更新调用。有兴趣的读者,可以瞄一眼
InputManagerService.java
看一下InputManagerService.java的构造体
frameworks/base/core/java/android/hardware/input/InputManagerService.java
public classInputManagerServer {
……..
public InputManagerService(Context context,Handler handler) {
this.mContext = context;
this.mHandler = new InputManagerHandler(handler.getLooper());
……..
mPtr = nativeInit(this, mContext,mHandler.getLooper().getQueue());
}
……..
}
InputManagerServer是由SystemServer直接启动的。
Handler是什么呢?
之前说这个从SystemServer传进来的。换言之,这里的Looper跟SystemServer传入的Looper是一样的,也就是WindowManager跟InputManager用了一个MessageQueue。那么,它在某种意义上算是消息同步的。
nativeInit是native层的方法,将结果又传递给java层的mPtr保存起来,在后续一定会有相关的转换调用。
nativeInit在com_android_server_input_InputManagerService.cpp中
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
static jint 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 notinitialized.");
return 0;
}
NativeInputManager* im = newNativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jint>(im);
}
这里又将java层传入的MessageQueue转换,再次初始化NativeInputManager。
NativeInputManager::NativeInputManager(jobjectcontextObj,
jobject serviceObj, constsp<Looper>& looper) :
mLooper(looper) {
…….
sp<EventHub> eventHub = newEventHub();
mInputManager = new InputManager(eventHub,this, this);
}
将looper保存到native层的mLooper。实例话了EventHub,将其作为参数传递给InputManager实例化。
这里有必要看一下EventHub的结构体。
EventHub::EventHub(void):
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),mNeedToScanDevices(true),
mPendingEventCount(0),mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_ID);
mEpollFd =epoll_create(EPOLL_SIZE_HINT);
mINotifyFd =inotify_init();
int result =inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
struct epoll_event eventItem;
memset(&eventItem, 0,sizeof(eventItem));
eventItem.events =EPOLLIN;
eventItem.data.u32= EPOLL_ID_INOTIFY;
result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
int wakeFds[2];
result =pipe(wakeFds);
mWakeReadPipeFd =wakeFds[0];
mWakeWritePipeFd =wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL,O_NONBLOCK);
result =fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32= EPOLL_ID_WAKE;
result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}
看标红的代码段:EventHub使用的epoll机制,inotify机制,pipe机制。
这段结构体有如下解释
1. 对一些成员变量做了初始化
2. 创建了epoll管道,EPOLL_SIZE_HINT代表的是最大监控数量
3. 调用inotify机制对/dev/input文件目录进行监听,任何增删改动作
4. 用epoll机制EPOLL_CTL_ADD命令把inotify的句柄和以及后面一句中的mWakeReadPipeFd句柄,加入到epoll的句柄mEpollFd中。这样,只要/dev/input下面有任何动作,就会在mWakeWritePipeFd中写数据,唤醒线程,epoll_wait就会被返回。
InputManager.cpp
frameworks/base/services/input/InputManager.cpp
InputManager::InputManager(
const sp<EventHubInterface>&eventHub,
constsp<InputReaderPolicyInterface>& readerPolicy,
constsp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = newInputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub,readerPolicy, mDispatcher);
initialize();
}
这里实例化了InputDispatcher和InputReader。
InputDispatcher:从这里看不出具体的,猜测是按键消息的分发给当前激活的activity。比如dispatchTouchEvent之类就是分发
InputReader:同样看不出来,但是,结合eventHub,readerPolicy,可能是读取键盘事件。
事实证明猜测是正确的。后续分析。。。。
voidInputManager::initialize() {
mReaderThread = newInputReaderThread(mReader);
mDispatcherThread = newInputDispatcherThread(mDispatcher);
}
这里,又将实例化的InputDispatcher和InputReader,再次传给InputDispatcherThread和InputReaderThread作为参数。这里的两个线程实例一个是用来读取键盘事件,而另一个是用来分发键盘事件的消息。
至此,键盘事件的初始化就完成了。
文件结构如下:
小结:
1. SystemServer在initandloop中实例化了InputManagerServer,这是用来接收分派按键事件的service
2. SystemServer将InputManagerServer传递给WindowManagerService控制输入法的显示,以及获取相关状态
3. C++层也相应地创建一个InputManager的实例会监控处理键盘事件
4. InputManager用InputReader和InputDispatcher分别处理键盘事件的读取以及消息分发
5. InputReader和InputDispatcher各自有一个线程来做相关的工作
事件分发和获取
对整体的结构有个大概的样子之后,开始了解键盘事件的处理方法
重新回到SystemServer
frameworks/base/services/java/com/android/server/SystemServer.java
Class SystemServer{
Publicvoid initAndLoop(){
……..
inputManager= new InputManagerService(context, wmHandler);
………
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
……
}
}
InputManagerServer被实例化后,又被调用了start方法。
来看下start方法
切换到InputManagerServer.java
frameworks/base/core/java/android/hardware/input/InputManagerService.java
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
}
这里,将直接init出来的mPtr作为参数传递给native层的nativestart方法。
registerPointerSpeedSettingObserver:这是监听Settings.System.POINTER_SPEED,一旦改变就修改对应的触摸监听频率
registerShowTouchesSettingObserver:这是监听Settings.System.SHOW_TOUCHES,这个具体是什么用的。不详。有兴趣的读者可以了解下。
这里同时监听了Intent.ACTION_USER_SWITCHED,若用户切换,那么重新调用updatePointerSpeedFromSettings(); updateShowTouchesFromSettings();
updatePointerSpeedFromSettings();:更新系统属性值Settings.System.POINTER_SPEED,并设置
updateShowTouchesFromSettings();:更新系统属性值Settings.System.SHOW_TOUCHES,并设置
重点看一下nativeStart,其他的都是浮云啊。
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
…
inlinesp<InputManager> getInputManager() const { return mInputManager; }
…….
static voidnativeStart(JNIEnv* env, jclass clazz, jint ptr) {
NativeInputManager* im =reinterpret_cast<NativeInputManager*>(ptr);
status_t result =im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env,"Input manager could not be started.");
}
}
……….
这里将java的mPtr转换成NativeInputManager,继而获得在native层保存的mInputManager,调用其start方法。
继续看InputManager.cpp
status_tInputManager::start() {
status_t result =mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);
……….
result =mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
……..
}
这里的start方法,将mDispatcherThread和mReaderThread运行起来,同时给了PRIORITY_URGENT_DISPLAY这个级别跟SurfaceFlinger的priority是一样.可见是及时响应的
接下来看下,InputDispatcherThread
frameworks/base/services/input/InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
为什么这个Thread这么奇葩,因为android对thread有封装,有兴趣的话,可以了解下。
这里mDispatcher就是在实例化InputDispatcherThread作为参数传入的InputDispatcher。
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{// acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake upimmediately.
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
}// 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:分发键盘事件-----稍后分析,这部分会从上到下涉及面相当大。先将如何读取信息的部分看一下。
pollOnce:获取键盘事件,如果没有消息的话就是阻塞,底层的实现是基于pipe机制的。
小结下,InputDispatch会调用dispatchOnceInnerLocked去分发消息;在消息分完完成后,又使用mLooper继续获取消息。当mLooper.pollOnce有读取到消息的时候就会往管道里头写新的内容,唤醒正在等待键盘事件的线程;当没有消息的时候,就无限的阻塞着,线程进入空闲等待状态。
事件读取
先说InputReader。为什么先这个,因为这个短小精悍,InputDispatcher太长了。处理了InputReader会对后续InputDispatcher也有帮助。
InputReaderThread
frameworks/base/services/input/ InputReaderThread.cpp
boolInputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
这里调用了InputReader的loopOnce方法。
void InputReader::loopOnce() {
………….
size_t count = mEventHub->getEvents(timeoutMillis,mEventBuffer, EVENT_BUFFER_SIZE);
{// acquire lock
……….
if (count) {
processEventsLocked(mEventBuffer, count);
}
…………………
}
}
这里,从mEventHub中获得了event并存在mEventBuffer中,然后,将mEventBuffer和count作为参数传递给processEventsLocked处理。
看下getEvent是怎么获取的?
/frameworks/base/services/input/EventHub.cpp
size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now =systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
…………
closeAllDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark
mNeedToScanDevices = true;
break; // return to the callerbefore we actually rescan
}//#这里的mNeedToReopenDevices标记为true,那么就关闭所有的设备,并标记mNeedToScanDevices为true,重新扫描设备
// Report any devices that had lastbeen added/removed.
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting deviceclosed: id=%d, name=%s\n",
device->id,device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id== mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan =true;
if (--capacity == 0) {
break;
}
}//#若mClosingDevices不为空,那么将所有的mClosingDevices重置,并删除。
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark
mNeedToSendFinishedDeviceScan =true;
}//#若mNeedToScanDevices判定为true,那么调用scanDevicesLocked
while (mOpeningDevices != NULL) {
Device* device = mOpeningDevices;
ALOGV("Reporting deviceopened: id=%d, name=%s\n",
device->id,device->path.string());
mOpeningDevices = device->next;
event->when = now;
event->deviceId = device->id== mBuiltInKeyboardId ? 0 : device->id;
event->type = DEVICE_ADDED;
event += 1;
mNeedToSendFinishedDeviceScan =true;
if (--capacity == 0) {
break;
}
}
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan =false;
event->when = now;
event->type =FINISHED_DEVICE_SCAN;
event += 1;
if (--capacity == 0) {
break;
}
}
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex <mPendingEventCount) {
const struct epoll_event&eventItem = mPendingEventItems[mPendingEventIndex++];
if (eventItem.data.u32 ==EPOLL_ID_INOTIFY) {
if (eventItem.events &EPOLLIN) {
mPendingINotify = true;
} else {
ALOGW("Receivedunexpected epoll event 0x%08x for INotify.", eventItem.events);
}
continue;
}
if (eventItem.data.u32 ==EPOLL_ID_WAKE) {
if (eventItem.events &EPOLLIN) {
ALOGV("awoken afterwake()");
awoken = true;
char buffer[16];
ssize_t nRead;
do {
nRead =read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1&& errno == EINTR) || nRead == sizeof(buffer));
} else {
ALOGW("Receivedunexpected epoll event 0x%08x for wake read pipe.",
eventItem.events);
}
continue;
}
ssize_t deviceIndex =mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0) {
ALOGW("Received unexpectedepoll event 0x%08x for unknown device id %d.",
eventItem.events,eventItem.data.u32);
continue;
}
Device* device =mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN){
int32_t readSize =read(device->fd, readBuffer,
sizeof(struct input_event)* capacity);
if (readSize == 0 || (readSize< 0 && errno == ENODEV)) {
// Device was removedbefore INotify noticed.
ALOGW("could not getevent, removed? (fd: %d size: %d bufferSize: %d "
"capacity: %d errno:%d)\n",
device->fd,readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
} else if (readSize < 0) {
if (errno != EAGAIN&& errno != EINTR) {
ALOGW("could notget event (errno=%d)", errno);
}
} else if ((readSize %sizeof(struct input_event)) != 0) {
ALOGE("could not get event (wrong size:%d)", readSize);
} else {
int32_t deviceId =device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count =size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i <count; i++) {
struct input_event&iev = readBuffer[i];
ALOGV("%s got:time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int)iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type,iev.code, iev.value);
………..
if (iev.type == EV_MSC){
if (iev.code == MSC_ANDROID_TIME_SEC){
device->timestampOverrideSec = iev.value;
continue;
} else if (iev.code== MSC_ANDROID_TIME_USEC) {
device->timestampOverrideUsec= iev.value;
continue;
}
}
if(device->timestampOverrideSec || device->timestampOverrideUsec) {
iev.time.tv_sec =device->timestampOverrideSec;
iev.time.tv_usec =device->timestampOverrideUsec;
if (iev.type ==EV_SYN && iev.code == SYN_REPORT) {
device->timestampOverrideSec = 0;
device->timestampOverrideUsec = 0;
}
ALOGV("appliedoverride time %d.%06d",
int(iev.time.tv_sec), int(iev.time.tv_usec));
}
#ifdefHAVE_POSIX_CLOCKS
……….
#else
event->when = now;
#endif
event->deviceId =deviceId;
event->type =iev.type;
event->code =iev.code;
event->value =iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
// The result buffer isfull. Reset the pending event index
// so we will try toread the device again on the next iteration.
mPendingEventIndex -=1;
break;
}
}
} else if (eventItem.events &EPOLLHUP) {
ALOGI("Removing device %sdue to epoll hang-up event.",
device->identifier.name.string());
deviceChanged = true;
closeDeviceLocked(device);
} else {
ALOGW("Received unexpectedepoll event 0x%08x for device %s.",
eventIte