之前分析按键的博客,我们分析了按键的流程,但是对按键具体在InputFlinger中分发给哪个进程这块没有分析。
WMS设置焦点
我们先来看WMS的updateFocusedWindowLocked函数,当Window窗口或者焦点有变化都会调用这个函数,这个函数先调用findFocusedWindowLocked来获取当前有焦点的窗口,然后会调用InputMonitor的setInputFocusLw函数.
我们先来看findFocusedWindowLocked函数。这个函数把WindowList逆序遍历,把最前面的window放在前面遍历,然后找该window的mAppToken是否在各个Task中存在,存在就认为当前window就是有焦点的window。
private WindowState findFocusedWindowLocked(DisplayContent displayContent) { final WindowList windows = displayContent.getWindowList(); for (int i = windows.size() - 1; i >= 0; i--) { final WindowState win = windows.get(i); if (localLOGV || DEBUG_FOCUS) Slog.v( TAG, "Looking for focus: " + i + " = " + win + ", flags=" + win.mAttrs.flags + ", canReceive=" + win.canReceiveKeys()); if (!win.canReceiveKeys()) { continue; } AppWindowToken wtoken = win.mAppToken; // If this window's application has been removed, just skip it. if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) { if (DEBUG_FOCUS) Slog.v(TAG, "Skipping " + wtoken + " because " + (wtoken.removed ? "removed" : "sendingToBottom")); continue; } // Descend through all of the app tokens and find the first that either matches // win.mAppToken (return win) or mFocusedApp (return null). if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING && mFocusedApp != null) { ArrayList<Task> tasks = displayContent.getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { AppTokenList tokens = tasks.get(taskNdx).mAppTokens; int tokenNdx = tokens.size() - 1; for ( ; tokenNdx >= 0; --tokenNdx) { final AppWindowToken token = tokens.get(tokenNdx); if (wtoken == token) { break; } if (mFocusedApp == token) { // Whoops, we are below the focused app... no focus for you! if (localLOGV || DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: Reached focused app=" + mFocusedApp); return null; } } if (tokenNdx >= 0) { // Early exit from loop, must have found the matching token. break; } } } if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: Found new focus @ " + i + " = " + win); return win; } if (DEBUG_FOCUS_LIGHT) Slog.v(TAG, "findFocusedWindow: No focusable windows."); return null; }
setInputFocusLw函数中,把当前有焦点的window保存在mInputFocus中,然后调用updateInputWindows函数。
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { if (WindowManagerService.DEBUG_FOCUS_LIGHT || WindowManagerService.DEBUG_INPUT) { Slog.d(WindowManagerService.TAG, "Input focus has changed to " + newWindow); } if (newWindow != mInputFocus) { if (newWindow != null && newWindow.canReceiveKeys()) { // Displaying a window implicitly causes dispatching to be unpaused. // This is to protect against bugs if someone pauses dispatching but // forgets to resume. newWindow.mToken.paused = false; } mInputFocus = newWindow; setUpdateInputWindowsNeededLw(); if (updateInputWindows) { updateInputWindowsLw(false /*force*/); } } }我们再看看updateInputWindowsLw函数,遍历所有的窗口,把是当前有焦点的窗口hasFocus置为true,然后调用addInputWindowHandleLw函数把所有的InputWindowHandle放入mInputWindowHandles中,最后调用InputManagerService的setInputWindows,设置到InputFlinger中去。
public void updateInputWindowsLw(boolean force) { if (!force && !mUpdateInputWindowsNeeded) { return; } ...... // Add all windows on the default display. final int numDisplays = mService.mDisplayContents.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList(); for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {//遍历所有设备上的窗口 final WindowState child = windows.get(winNdx); final InputChannel inputChannel = child.mInputChannel; final InputWindowHandle inputWindowHandle = child.mInputWindowHandle; if (inputChannel == null || inputWindowHandle == null || child.mRemoved) { // Skip this window because it cannot possibly receive input. continue; } if (addInputConsumerHandle && inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) { addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle); addInputConsumerHandle = false; } final int flags = child.mAttrs.flags; final int privateFlags = child.mAttrs.privateFlags; final int type = child.mAttrs.type; final boolean hasFocus = (child == mInputFocus);//是有焦点的窗口,把hasFocus置为true final boolean isVisible = child.isVisibleLw(); if ((privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) { disableWallpaperTouchEvents = true; } final boolean hasWallpaper = (child == mService.mWallpaperTarget) && (privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD) == 0 && !disableWallpaperTouchEvents; final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY); // If there's a drag in progress and 'child' is a potential drop target, // make sure it's been told about the drag if (inDrag && isVisible && onDefaultDisplay) { mService.mDragState.sendDragStartedIfNeededLw(child); } addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper); } } // Send windows to native code. mService.mInputManager.setInputWindows(mInputWindowHandles);//调用InputManagerService的setInputWindows // Clear the list in preparation for the next round. clearInputWindowHandlesLw(); }这个函数就是对InputWindowHandle的变量进行赋值,然后调用了另一个addInputWindowHandleLw函数
private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper) { // Add a window to our list of input windows. inputWindowHandle.name = child.toString(); final boolean modal = (flags & (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) == 0; if (modal && child.mAppToken != null) { // Limit the outer touch to the activity stack region. flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; child.getStackBounds(mTmpRect); inputWindowHandle.touchableRegion.set(mTmpRect); } else { // Not modal or full screen modal child.getTouchableRegion(inputWindowHandle.touchableRegion); } inputWindowHandle.layoutParamsFlags = flags; inputWindowHandle.layoutParamsType = type; inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos(); inputWindowHandle.visible = isVisible; inputWindowHandle.canReceiveKeys = child.canReceiveKeys(); inputWindowHandle.hasFocus = hasFocus; inputWindowHandle.hasWallpaper = hasWallpaper; inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false; inputWindowHandle.layer = child.mLayer; inputWindowHandle.ownerPid = child.mSession.mPid; inputWindowHandle.ownerUid = child.mSession.mUid; inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; final Rect frame = child.mFrame; inputWindowHandle.frameLeft = frame.left; inputWindowHandle.frameTop = frame.top; inputWindowHandle.frameRight = frame.right; inputWindowHandle.frameBottom = frame.bottom; if (child.mGlobalScale != 1) { // If we are scaling the window, input coordinates need // to be inversely scaled to map from what is on screen // to what is actually being touched in the UI. inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale; } else { inputWindowHandle.scaleFactor = 1; } addInputWindowHandleLw(inputWindowHandle); }这个addInputWindowHandleLw函数就是把所有的InputWindowHandle放入mInputWindowHandles中。
private void addInputWindowHandleLw(final InputWindowHandle windowHandle) { if (mInputWindowHandles == null) { mInputWindowHandles = new InputWindowHandle[16]; } if (mInputWindowHandleCount >= mInputWindowHandles.length) { mInputWindowHandles = Arrays.copyOf(mInputWindowHandles, mInputWindowHandleCount * 2); } mInputWindowHandles[mInputWindowHandleCount++] = windowHandle; }
在之前博客我们分析过InputChannel是在addWIindow中创建的,创建了InputChannel之后直接把InputChannel[0]作为参数调用了WindowState的setInputChannel函数。
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); }WindowState的setInputChannel函数直接把InputWindowHandle的InputChannel对象设置为之前创建的InputChannel[0].
void setInputChannel(InputChannel inputChannel) { if (mInputChannel != null) { throw new IllegalStateException("Window already has an input channel."); } mInputChannel = inputChannel; mInputWindowHandle.inputChannel = inputChannel; }
InputDispatcher的mFocusedWindowHandle
InputManagerService的setInputWindows函数直接调用了JNI函数。public void setInputWindows(InputWindowHandle[] windowHandles) { nativeSetInputWindows(mPtr, windowHandles); }然后从NativeInputManager::setInputWindows函数中再调用了InputDispatcher的setInputWindows函数,这个函数就是遍历所有的InputWindowHandle看是谁的hasFocus为true,为true最后把mFocusedWindowHandle设置为这个InputWindowHandle。
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { { // acquire lock AutoMutex _l(mLock); Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles; mWindowHandles = inputWindowHandles; sp<InputWindowHandle> newFocusedWindowHandle; bool foundHoveredWindow = false; for (size_t i = 0; i < mWindowHandles.size(); i++) { const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) { mWindowHandles.removeAt(i--); continue; } if (windowHandle->getInfo()->hasFocus) { newFocusedWindowHandle = windowHandle; } if (windowHandle == mLastHoverWindowHandle) { foundHoveredWindow = true; } } if (!foundHoveredWindow) { mLastHoverWindowHandle = NULL; } ...... }
分发按键
我们最后在InputDispatcher中分发按键时,会到dispatchKeyLocked函数,这个函数会调用函数findFocusedWindowTargetsLocked函数,这个函数又会调用addWindowTargetLocked函数,mFocusWindowHandle就是有焦点的那个window。inputTargets是个输出参数。
addWindowTargetLocked(mFocusedWindowHandle, InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), inputTargets);addWindowTargetLocked函数就是把InputWindowHandle的一个成员变量InputWindowInfo的值给InputTarget。
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) { inputTargets.push(); const InputWindowInfo* windowInfo = windowHandle->getInfo(); InputTarget& target = inputTargets.editTop(); target.inputChannel = windowInfo->inputChannel; target.flags = targetFlags; target.xOffset = - windowInfo->frameLeft; target.yOffset = - windowInfo->frameTop; target.scaleFactor = windowInfo->scaleFactor; target.pointerIds = pointerIds; }然后在dispatchEventLocked函数中调用getConnectionIndexLocked获取Connection,最后再和应用进行通信,把按键发到应用进程的ViewRootImpl中。
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { pokeUserActivityLocked(eventEntry); for (size_t i = 0; i < inputTargets.size(); i++) { const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } } }getConnectionIndexLocked就是获取Connection在mConnectionByFd的index。
ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd()); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->inputChannel.get() == inputChannel.get()) { return connectionIndex; } } return -1; }