InputManagerService
原理
在SystemServer
的startOtherServices
里面开启IMS
private void startOtherServices() {
...
InputManagerService inputManager = null;
...
traceBeginAndSlog("StartInputManagerService");
inputManager = new InputManagerService(context);
traceEnd();
...
traceBeginAndSlog("StartInputManager");
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
traceEnd();
}
初始化的时候,初始化了一个Handler
,用的DisplayThread
的Looper
,把消息列表传入native
方法里面,调用了nativeInit
初始化的方法
public InputManagerService(Context context) {
this.mContext = context;
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
初始化 EventHub
与 InputManager
,其中 EventHub
的本质要封装了 epoll
与 inotify
,监听设备写入的事件并读取到缓冲中进行简单解析
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
...
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
epoll
初始化完成后,当输入设备被写入数据后,epoll_wait
就会返回数据,并可以对其进行解析
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);
// 新建一个 epoll
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
// 初始化 inotify
mINotifyFd = inotify_init();
// 监听设备的创建与删除
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
// 增加需要监听的设备 fd
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
...
}
继续看start方法,start方法里面调用了nativeStart
方法的初始化,和其他系统服务一样把当前对象添加到了Watchdog里面监听,因为系统服务一旦挂了,整个android系统就无法运行,就会强制重启,其原理就是通过添加到Watchdog
去监测。具体Watchdog
原理可以看这篇从源码角度看 WatchDog 机制
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
...
}
nativeStart主要是调用native里面inputManager的start方法
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
InputManager
启动了两个 native 线程,一个是InputReadThread
,负责从驱动读取Event
,一个是InputDispatcherThread
,负责分发。
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
return OK;
}
事件的读取,InputReaderThread,调用InputReader的loopOnce方法
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
loopOne
方法中,EventHub.getEvents
如果获取到了数据才会进行到下一个状态中,大部分时间里,如果没有输入事件 getEvents 将会产生阻塞,不会消耗 CPU 资源。
InputReader
并没有直接去访问设备节点,而是通过EventHub
来处理
EventHub
通过读取/dev/input/
下的相关文件来判断是否有新事件,并在EventHub.getEvents
处返回,类似MessageQueue
里面的next方法一样。
void InputReader::loopOnce() {
...
// 从 EventHub 中读取数据
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
// 如果有数据则进行处理
processEventsLocked(mEventBuffer, count);
}
...
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
...
// 讲添加进缓存队列中的事件一齐插入到 InputDispatcher 的队列中
mQueuedListener->flush();
}
现在来看事件的分发,在InputDispatcherThread
线程里面,同样调用mDispatcher
的dispatchOnce
方法
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
方法里面调用了dispatchOnceInnerLocked
方法
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
...
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
...
} // 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);
}
里调用了dispatchMotionLocked
方法
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
...
} else {
mPendingEvent = mInboundQueue.dequeueAtHead();
}
}
...
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
...
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
}
dispatchMotionLocked
方法里面调用了,
findTouchedWindowTargetsLocked方法,确定当前接收事件的有焦点的接收方(也就是客户端需要处理事件的窗口),其中还包括对 ANR 的判断 ,如果监测到派发的时间超过了 5s 还没有处理完成的话,native 就会通知 java 层需要触发 ANR 找到接收事件作用的客户端后保存在
inputTargets` 列表中
方法调用dispatchEventLocked方法
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
Vector<InputTarget> inputTargets;
...
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
// Non touch event. (eg. trackball)
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
dispatchEventLocked(currentTime, entry, inputTargets);
}
那么,InputDispatcher
为什么会有当前窗口的句柄呢?我们知道窗口的管理是WindowManagerService
,也就是说拿到WMS的引用应该就可以获取到当前的窗口是哪个,所以IMS里面只要有个WMS引用就可以,事实是什么样呢,继续看代码
IMS并没有直接持有WMS,而是有个mFocusedWindow
属性,这个就是我们客户端添加窗口是添加到WMS的,
private IWindow mFocusedWindow;
在setInputWindows方法里面赋值的,那这个setInputWindow又是谁赋值的?
public void setInputWindows(InputWindowHandle[] windowHandles,
InputWindowHandle focusedWindowHandle) {
final IWindow newFocusedWindow =
focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
if (mFocusedWindow != newFocusedWindow) {
mFocusedWindow = newFocusedWindow;
if (mFocusedWindowHasCapture) {
setPointerCapture(false);
}
}
nativeSetInputWindows(mPtr, windowHandles);
}
在InputMonitoer
的内部类UpdateInputForAllWindowsConsumer
里面,mService即WMS,mInputManager
即IMS,直接调用了WMS的IMS的setInputWindows
的方法,也就是说WMS是直接持有IMS的引用的。
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
private void updateInputWindows(boolean inDrag) {
...
// Send windows to native code.
mService.mInputManager.setInputWindows(mInputWindowHandles,
}
}
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
WindowManagerPolicy policy) {
mInputManager = inputManager; // Must be before createDisplayContentLocked.
}
继续往调用的地方找,在InputMonitor
的方法updateInputWindowsLw
里面
void updateInputWindowsLw(boolean force) {
...
mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
}
继续往调用的地方找,在WMS的addWindow方法里面调用,addWindow不正是我们客户端添加窗口的调用的方法
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
...
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
...
return res;
}
虽然WMS持有IMS,但是它不是直接操作,而是通过InputMonitor,把窗口传递到IMS
继续看InputDispatcher::dispatchEventLocked
方法
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
...
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
//获取到的 Connection
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
...
}
}
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
...
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
获取到当前窗口,是为了知道当前是哪个客户端需要接收事件。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
...
case EventEntry::TYPE_MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
...
// Publish the motion event.
status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
motionEntry->deviceId, motionEntry->source,
dispatchEntry->resolvedAction, motionEntry->actionButton,
dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
motionEntry->metaState, motionEntry->buttonState,
xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, motionEntry->eventTime,
motionEntry->pointerCount, motionEntry->pointerProperties,
usingCoords);
break;
}
...
}
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) :
status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle),
monitor(monitor),
inputPublisher(inputChannel), inputPublisherBlocked(false) {
}
获取到的 Connection,调用 InputPublishser 的publishMotionEvent 方法将事件传输到客户端.这里看一下 Connection 的实现,其实就是封装了 InputChannel 成员变量与 window 句柄的类
那么InputChannel
什么时候设置过来的呢
在WMS的addWindow方法里面
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
...
}
openInputChannelPair 会调用 native 方法初始化 socketpair,registerInputChannel 则是将 inputChannel 句柄传入 native 给 InputDispatcher
再来看InputPublisher.publishMotionEvent方法
status_t InputPublisher::publishMotionEvent(...) {
InputMessage msg;
...
for (uint32_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
}
return mChannel->sendMessage(&msg);
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
...
}
通过将信息封装成 InputMessage,随后使用 InputChannel.sendMessage 将信息发送到客户端`
同样的应用程序这边的InputChannel是什么时候初始化的呢
在ViewRootImpl
的setView
方法里面
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
}
ViewRootImpl.setView 时,与WMS 通信完成之后会去初始化 InputChannel 的客户端
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
...
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
通过调用 nativeInit 在 native 层进行初始化
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
...
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
...
return reinterpret_cast<jlong>(receiver.get());
}
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
真正的实现是在 NativeInputEventReceiver.initialize 方法进行的,最终使用的是传入的 MessageQueue 中的 Looper 来监听 InputChannel 客户端的 fd 实现的,本质上还是使用了 epoll 机制来监听 fd 的可读状态。当事件到来时,会调用回调方法 handleEvent 来处理事件
网友评论