之前分析按键的博客,我们分析了按键的流程,但是对按键具体在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;
}