在工作中,不乏一些做稳定性的"小朋友",当遇到无法解决的"无焦点窗口"的ANR时,就会与我做一番"友好"的交流,有时候真让我不胜其烦。说实话,这些东西都是做稳定性的基本功。
本文先来分析焦点窗口的更新框架,后面将分析 app 冷启动的焦点窗口更新流程,最后再来探讨下焦点窗口与"无焦点窗口" ANR 的关系。
更新焦点窗口
一般来说,当出现新窗口时,如果窗口可以接收按键事件,那么它就可以成为焦点窗口。此时需调用 WindowManagerService#updateFocusedWindowLocked() 来更新焦点窗口,如下
java
// WindowManagerService.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changed;
}
java
// RootWindowContainer.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
// ...
// RootWindowContainer 遍历所有 DisplayContent,更新它们的焦点窗口
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
// 更新每一个 DisplayContent 的焦点窗口
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
// ...
}
// ...
return changed;
}
java
// DisplayContent.java
/**
* Update the focused window and make some adjustments if the focus has changed.
*
* @param mode Indicates the situation we are in. Possible modes are:
* {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
* {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
* {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
* {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
* @param updateInputWindows Whether to sync the window information to the input module.
* @param topFocusedDisplayId Display id of current top focused display.
* @return {@code true} if the focused window has changed.
*/
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
int topFocusedDisplayId) {
// shell-transitions 开启的情况下,近期任务动画的焦点窗口,不是在这里自动更新的
// Don't re-assign focus automatically away from a should-keep-focus app window.
// `findFocusedWindow` will always grab the transient-launch app since it is "on top" which
// would create a mismatch, so just early-out here.
if (mCurrentFocus != null && mTransitionController.shouldKeepFocus(mCurrentFocus)
// This is only keeping focus, so don't early-out if the focused-app has been
// explicitly changed (eg. via setFocusedTask).
&& mFocusedApp != null && mCurrentFocus.isDescendantOf(mFocusedApp)
&& mCurrentFocus.isVisible() && mCurrentFocus.isFocusable()) {
ProtoLog.v(WM_DEBUG_FOCUS, "Current transition prevents automatic focus change");
return false;
}
// 1. 获取 DisplayContent 下的焦点窗口
WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
if (mCurrentFocus == newFocus) {
return false;
}
// ...
// WMS 焦点窗口改变的 log
ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
// mCurrentFocus 保存的就是 DisplayContent 的焦点窗口
mCurrentFocus = newFocus;
// ...
// DisplayPolicy 主要使用 mFocusedWindow 保存新焦点窗口,
// 使用 mLastFocusedWindow 保存前一个焦点窗口
getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
// ...
// 2. mode 不是 UPDATE_FOCUS_WILL_ASSIGN_LAYERS 时,才向输入系统更新焦点窗口
// add window 时的焦点窗口更新,使用的就是 UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
// 然后更新 layer,最后补一个焦点窗口更新。所以,这里不会更新焦点窗口
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for doing this part.
getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
}
// ...
return true;
}
DisplayContent 会先获取焦点窗口,然后通过 InputMonitor ,把焦点窗口设置给底层。
获取焦点窗口
java
// DisplayContent.java
WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) {
return (hasOwnFocus() || topFocusedDisplayId == INVALID_DISPLAY)
? findFocusedWindow() : null;
}
WindowState findFocusedWindow() {
mTmpWindow = null;
// 从上到下遍历所有窗口,获取焦点窗口,并保存到 mTmpWindow
// mFindFocusedWindow will populate mTmpWindow with the new focused window when found.
forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
if (mTmpWindow == null) {
// 没有找到焦点窗口的 log
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d",
getDisplayId());
return null;
}
return mTmpWindow;
}
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
// 当前焦点 app
final ActivityRecord focusedApp = mFocusedApp;
// 寻找焦点窗口的 log
ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
w, w.mAttrs.flags, w.canReceiveKeys(),
w.canReceiveKeysReason(false /* fromUserTouch */));
// 无法接受按键事件的窗口,无法成为焦点窗口
if (!w.canReceiveKeys()) {
return false;
}
// ...
// 窗口所属的 Activity
final ActivityRecord activity = w.mActivityRecord;
// 当前 DisplayContent 没有焦点 app,那么就让当前遍历的窗口成为焦点窗口
if (focusedApp == null) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
"findFocusedWindow: focusedApp=null using new focus @ %s", w);
mTmpWindow = w;
return true;
}
// 焦点 app 下的所有窗口都不能获得焦点,那么让当前遍历的窗口,成为焦点窗口
if (!focusedApp.windowsAreFocusable()) {
// Current focused app windows aren't focusable...
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: focusedApp windows not"
+ " focusable using new focus @ %s", w);
mTmpWindow = w;
return true;
}
// 可以看到,启动窗口是不能成为焦点窗口的
// Descend through all of the app tokens and find the first that either matches
// win.mActivityRecord (return win) or mFocusedApp (return null).
if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
// 如果窗口的 activity 的 layer 低于当前焦点 app 的 layer,那么这个窗口不能成为焦点窗口
if (focusedApp.compareTo(activity) > 0) {
// App root task below focused app root task. No focus for you!!!
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
"findFocusedWindow: Reached focused app=%s", focusedApp);
mTmpWindow = null;
return true;
}
// If the candidate activity is currently being embedded in the focused task, the
// activity cannot be focused unless it is on the same TaskFragment as the focusedApp's.
TaskFragment parent = activity.getTaskFragment();
if (parent != null && parent.isEmbedded()) {
if (activity.getTask() == focusedApp.getTask()
&& activity.getTaskFragment() != focusedApp.getTaskFragment()) {
return false;
}
}
}
// 找到焦点窗口的 log
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
// 保存焦点窗口
mTmpWindow = w;
return true;
};
从上到下遍历 DisplayContent 的所有窗口,一般来说,只要窗口可以接收按键事件,那么就可以成为焦点窗口。
窗口可以接收事件的条件有点多,我大致标注下,如下
java
// WindowState.java
boolean canReceiveKeys() {
return canReceiveKeys(false /* fromUserTouch */);
}
public boolean canReceiveKeys(boolean fromUserTouch) {
// 近期任务动画,比较特殊
if (mActivityRecord != null && mTransitionController.shouldKeepFocus(mActivityRecord)) {
// During transient launch, the transient-hide windows are not visibleRequested
// or on-top but are kept focusable and thus can receive keys.
return true;
}
// 主要是下面这些条件,用于判断窗口是否可以接收按键事件
final boolean canReceiveKeys = isVisibleRequestedOrAdding()
// app 端 View 可见
&& (mViewVisibility == View.VISIBLE)
// mRemoveOnExit 我前面文章解释过,表示在 exit animation 后移除窗口
&& !mRemoveOnExit
// 不能设置 FLAG_NOT_FOCUSABLE
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
// can it receive touches
&& (mActivityRecord == null || mActivityRecord.getTask() == null
|| !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());
if (!canReceiveKeys) {
return false;
}
// Do not allow untrusted virtual display to receive keys unless user intentionally
// touches the display.
return fromUserTouch || getDisplayContent().isOnTop()
|| getDisplayContent().isTrusted();
}
boolean isVisibleRequestedOrAdding() {
final ActivityRecord atoken = mActivityRecord;
// 有 surface 或者 可见并且正在添加窗口
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
// 策略可见
&& isVisibleByPolicy()
// 父窗口不能隐藏
&& !isParentWindowHidden()
// ActivityRecord 请求可见
&& (atoken == null || atoken.isVisibleRequested())
// 没有执行 exit animation 动画
&& !mAnimatingExit
// 没有销毁窗口
&& !mDestroying;
}
设置焦点窗口
java
// InputMonitor.java
/**
* Called when the current input focus changes. Will apply it in next updateInputWindows.
* Layer assignment is assumed to be complete by the time this is called.
*/
void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
newWindow, mDisplayId);
final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null;
if (focus == mInputFocus) {
return;
}
if (newWindow != null && newWindow.canReceiveKeys()) {
newWindow.mToken.paused = false;
}
// 仅仅是把标志位 mUpdateInputWindowsNeeded 设置为 true
setUpdateInputWindowsNeededLw();
// 如果函数参数 updateInputWindows 为 true,才能调度去更新输入窗口信息到输入系统
// 在这期间,会更新焦点窗口给底层
if (updateInputWindows) {
updateInputWindowsLw(false /*force*/);
}
}
DisplayContent 焦点窗口改变后,会通过 InputMonitor 下发给底层。具体的方法是,先设置标志为 mUpdateInputWindowsNeeded 为 true,表示需要更新输入窗口给底层,然后调度更新输入窗口,来完成发送焦点窗口给底层。
更新输入窗口
一般来说,只要窗口改变,例如可见性改变,就需要向底层更新输入窗口,这其中也伴随着下发焦点窗口,如下
java
// InputMonitor.java
void updateInputWindowsLw(boolean force) {
// 要么强制更新,要么设置 mUpdateInputWindowsNeeded 标志位后更新
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
scheduleUpdateInputWindows();
}
private void scheduleUpdateInputWindows() {
if (mDisplayRemoved) {
return;
}
if (!mUpdateInputWindowsPending) {
mUpdateInputWindowsPending = true;
// mHandler 使用的是动画线程
mHandler.post(mUpdateInputWindows);
}
}
private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
private class UpdateInputWindows implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
mUpdateInputWindowsPending = false;
mUpdateInputWindowsNeeded = false;
if (mDisplayRemoved) {
return;
}
final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
// Add all windows on the default display.
mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
}
}
}
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
// ...
// 1. 如果窗口有改变,收集它的信息
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
// 2. 更新焦点窗口请求
updateInputFocusRequest(mRecentsAnimationInputConsumer);
// 3. DisplayContent pending transaction 合并 mInputTransaction ,然后在动画线程中 apply
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
更新输入窗口有三步
- 收集有改变的窗口的信息。
- 更新焦点窗口请求。
- 前两步的操作都是保存到 mInputTransaction,现在把它 merge 到 DisplayContent pending transaction,最后在动画线程中 apply,即下发到 SurfaceFlinger。
收集有改变的窗口的信息
java
// InputMonitor.java
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
public void accept(WindowState w) {
final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
// ...
// 前提条件是窗口必须有 surface
if (w.mWinAnimator.hasSurface()) {
// 1. 把输入窗口信息填充到 inputWindowHandle
populateInputWindowHandle(inputWindowHandle, w);
// 2. 如果窗口有改变,通过 mInputTransaction 保存窗口的信息
setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
}
}
}
DisplayContent 从上到下遍历所有窗口,先把窗口信息收集到 InputWindowHandleWrapper,如下
java
// InputMonitor.java
void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle,
final WindowState w) {
// Add a window to our list of input windows.
inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null
? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null);
inputWindowHandle.setToken(w.mInputChannelToken);
inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
inputWindowHandle.setWindowToken(w.mClient);
inputWindowHandle.setName(w.getName());
// Update layout params flags to force the window to be not touch modal. We do this to
// restrict the window's touchable region to the task even if it requests touches outside
// its window bounds. An example is a dialog in primary split should get touches outside its
// window within the primary task but should not get any touches going to the secondary
// task.
int flags = w.mAttrs.flags;
if (w.mAttrs.isModal()) {
flags = flags | FLAG_NOT_TOUCH_MODAL;
}
inputWindowHandle.setLayoutParamsFlags(flags);
inputWindowHandle.setInputConfigMasked(
InputConfigAdapter.getInputConfigFromWindowParams(
w.mAttrs.type, flags, w.mAttrs.inputFeatures),
InputConfigAdapter.getMask());
final boolean focusable = w.canReceiveKeys()
&& (mDisplayContent.hasOwnFocus() || mDisplayContent.isOnTop());
inputWindowHandle.setFocusable(focusable);
final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w)
&& !mService.mPolicy.isKeyguardShowing()
&& w.mAttrs.areWallpaperTouchEventsEnabled();
inputWindowHandle.setHasWallpaper(hasWallpaper);
// Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here.
inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left);
// 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.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
boolean useSurfaceBoundsAsTouchRegion = false;
SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
if (task != null) {
if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
// ...
} else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
// ...
}
}
inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
if (!useSurfaceBoundsAsTouchRegion) {
// 从 WindowState 中获取 touch region,一般就是 window frame 大小
w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
// 保存的是窗口的可触摸区域
inputWindowHandle.setTouchableRegion(mTmpRegion);
}
}
信息填充完毕后,会检测哪些窗口改变,然后通过 mInputTransaction 保存窗口的信息,如下
java
// InputMonitor.java
static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc,
InputWindowHandleWrapper inputWindowHandle) {
if (DEBUG_INPUT) {
Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle);
}
// 只有改变了,才需要把窗口信息更新到 SurfaceFlinger
if (inputWindowHandle.isChanged()) {
inputWindowHandle.applyChangesToSurface(t, sc);
}
}
java
// InputWindowHandleWrapper.java
void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) {
// 通过 mInputTransaction 保存有改变的窗口的信息
t.setInputWindowInfo(sc, mHandle);
mChanged = false;
}
java
// SurfaceControl.java
public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
checkPreconditions(sc);
nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
return this;
}
cpp
// frameworks/base/core/jni/android_view_SurfaceControl.cpp
static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobject inputWindow) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle(
env, inputWindow);
handle->updateInfo();
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}
java
// frameworks/native/libs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc, const WindowInfo& info) {
// 从 std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates 获取
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
s->windowInfoHandle = new WindowInfoHandle(info);
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
SurfaceComposerClient::Transaction 的 mComposerStates 保存了所有需要更新的 layer 的信息,之后 apply mInputTransaction 时,数据会发送到 SurfaceFlinger。
更新焦点窗口请求
DisplayContent 保存了所有有改变窗口的信息后,然后下发焦点窗口请求
java
// InputMonitor.java
private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {
// 获取 DisplayContent 的焦点窗口
final WindowState focus = mDisplayContent.mCurrentFocus;
if (recentsAnimationInputConsumer != null && focus != null) {
// ...
}
final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
if (focusToken == null) {
// ...
}
// 焦点窗口没有 surface 或者不能获得焦点,不需要设置焦点窗口到输入系统
if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus not requested for window=%s"
+ " because it has no surface or is not focusable.", focus);
mInputFocus = null;
return;
}
requestFocus(focusToken, focus.getName());
}
private void requestFocus(IBinder focusToken, String windowName) {
if (focusToken == mInputFocus) {
return;
}
// 保存向输入系统更新的焦点窗口 input channel token
mInputFocus = focusToken;
mInputFocusRequestTimeMillis = SystemClock.uptimeMillis();
// 通过 transaction 更新焦点窗口
mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId);
// WMS 向底层更新焦点窗口请求的 event log,例如
// input_focus: [Focus request f33c665 com.awesome.helloworld/com.awesome.helloworld.MainActivity,reason=UpdateInputWindows]
EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName,
"reason=UpdateInputWindows");
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName);
}
cpp
// android_view_SurfaceControl.cpp
static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
jobject toTokenObj, jstring windowNameJstr, jint displayId) {
// 转化为底层的 SurfaceComposerClient::Transaction
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
if (toTokenObj == NULL) return;
// token 转化为 IBinder
sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
// 创建 FocusRequest,保存传入的几个参数
FocusRequest request;
request.token = toToken;
if (windowNameJstr != NULL) {
ScopedUtfChars windowName(env, windowNameJstr);
request.windowName = windowName.c_str();
}
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
request.displayId = displayId;
// Transaction 保存 FocusRequest
transaction->setFocusedWindow(request);
}
cpp
// frameworks/native/libs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
mInputWindowCommands.focusRequests.push_back(request);
return *this;
}
SurfaceComposerClient::Transaction 使用 mInputWindowCommands.focusRequests 保存焦点窗口请求。
apply mInputTransaction
WMS 层的 Transaction 对应的是底层的 SurfaceComposerClient::Transaction,它的 apply 如下
cpp
// SurfaceComposerClient.cpp
// synchronous 为 false
// oneWay 默认为 false
status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
// ...
// mComposerStates 保存的是 layer 信息,现在复制到 composerStates
Vector<ComposerState> composerStates;
for (auto const& kv : mComposerStates) {
composerStates.add(kv.second);
}
// ...
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
// composerStates 保存了需要更新的窗口的信息
// mInputWindowCommands 保存了焦点窗口请求
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
mMergedTransactionIds);
// ...
// 清理 mComposerStates、mDisplayStates、mInputWindowCommands、mListenerCallbacks 等等
clear();
// ...
return NO_ERROR;
}
SurfaceComposerClient::Transaction 把很多数据发送给 SurfaceFlinger,其中 composerStates 保存了刚才更新的窗口的信息,mInputWindowCommands 保存了焦点请求。现在看下 SurfaceFlinger 如何处理
cpp
// SurfaceFlinger.cpp
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
const std::vector<uint64_t>& mergedTransactionIds) {
// ...
// 解析所有 layer,保存到 resolvedStates
std::vector<ResolvedComposerState> resolvedStates;
resolvedStates.reserve(states.size());
for (auto& state : states) {
resolvedStates.emplace_back(std::move(state));
// ...
}
// TransactionState 包装所有参数
TransactionState state{frameTimelineInfo,
resolvedStates,
displays,
flags,
applyToken,
std::move(inputWindowCommands),
desiredPresentTime,
isAutoTimestamp,
std::move(uncacheBufferIds),
postTime,
hasListenerCallbacks,
listenerCallbacks,
originPid,
originUid,
transactionId,
mergedTransactionIds};
// ...
// 保存到 mTransactionHandler.mLocklessTransactionQueue 队列中
mTransactionHandler.queueTransaction(std::move(state));
// 请求 Vsync 来处理 transaction
setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
return NO_ERROR;
}
void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
const sp<IBinder>& applyToken, FrameHint frameHint) {
mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
// 按位或更新 mTransactionFlags,并返回旧值
// mTransactionFlags 保存了 eTransactionFlushNeeded 标志位
uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
ATRACE_INT("mTransactionFlags", transactionFlags);
// 旧值 transactionFlags 不包含 mask 位,代表没有调度 VSync
if (const bool scheduled = transactionFlags & mask; !scheduled) {
scheduleCommit(frameHint);
} else if (frameHint == FrameHint::kActive) {
// ...
}
}
这里是以 Android U 的 SurfaceFlinger 代码进行分析的。而前面是以 Android V 代码分析的。
SurfaceFlinger 收到 apply transaction 的数据后,把数据包装到 TransactionState,然后保存到 TransactionHandler::mLocklessTransactionQueue 中,然后请求 Vsync 来处理 transcation。
当 VSync 信号到来,会先调用 SurfaceFlinger::commit() 准备数据,然后调用 SurfaceFlinger::composite() 合成 layer。 这里,我们主要看下 commit() 如何处理 transcation,如下
cpp
// SurfaceFlinger.cpp
bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
// ...
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(),
Fps::fromPeriodNsecs(vsyncPeriod.ns()));
// 清除并检测是否有 eTransactionFlushNeeded 标志位
// 前面已经设置过
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
frontend::Update updates;
if (flushTransactions) {
// 1. 收集需要更新的数据,例如 transcation
// 返回的 updates 保存了需要更新的数据
updates = flushLifecycleUpdates();
// ...
}
bool transactionsAreEmpty;
if (mLegacyFrontEndEnabled) {
// 2. 处理 updates 中的数据。
// 这里会 apply transcation 和 commit transaction
mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
transactionsAreEmpty);
}
// ...
}
// ...
// 3.把窗口信息,焦点窗口请求,更新到 InputFlinger
updateInputFlinger(vsyncId, frameTime);
// ...
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
这里简单过过一下 SurfaceFlinger 处理 transaction 过程,首先它会通过 flushLifecycleUpdates() 把需要更新的数据,收集到 frontend::Update。这些数据就保存 apply 的 transaction,如下
cpp
// SurfaceFlinger.cpp
frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
frontend::Update update;
ATRACE_NAME("TransactionHandler:flushTransactions");
// 收集 TransactionHandler:: mLocklessTransactionQueue 保存的 transaction
update.transactions = mTransactionHandler.flushTransactions();
{
// 这些收集的数据,与本文分析无关
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
update.layerCreatedStates = std::move(mCreatedLayers);
mCreatedLayers.clear();
update.newLayers = std::move(mNewLayers);
mNewLayers.clear();
update.layerCreationArgs = std::move(mNewLayerArgs);
mNewLayerArgs.clear();
update.destroyedHandles = std::move(mDestroyedHandles);
mDestroyedHandles.clear();
}
return update;
}
数据收集到 frontend::Update 后,开始处理这个数据,其中就包括 transcation
cpp
// SurfaceFlinger.cpp
bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
bool transactionsFlushed,
bool& outTransactionsAreEmpty) {
bool needsTraversal = false;
if (transactionsFlushed) {
needsTraversal |= commitMirrorDisplays(vsyncId);
needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
// 1. apply transaction 中的数据,
// 例如,更新 layer 数据,用 mInputWindowCommands 保存焦点窗口请求
needsTraversal |= applyTransactions(update.transactions, vsyncId);
}
outTransactionsAreEmpty = !needsTraversal;
const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
if (shouldCommit) {
// 2.准备数据,用于合成
commitTransactions();
}
bool mustComposite = latchBuffers() || shouldCommit;
updateLayerGeometry();
return mustComposite;
}
由于本文分析的 apply transaction,只包含需要更新的 layer,和焦点窗口请求,如下
cpp
// SurfaceFlinger.cpp
bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
std::vector<ResolvedComposerState>& states,
Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<uint64_t>& uncacheBufferIds,
const int64_t postTime, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
if (!mLayerLifecycleManagerEnabled) {
for (DisplayState& display : displays) {
// 处理 display change
transactionFlags |= setDisplayStateLocked(display);
}
}
// start and end registration for listeners w/ no surface so they can get their callback. Note
// that listeners with SurfaceControls will start registration during setClientStateLocked
// below.
for (const auto& listener : listenerCallbacks) {
mTransactionCallbackInvoker.addEmptyTransaction(listener);
}
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
if (mLegacyFrontEndEnabled) {
// 1.处理 layer change
clientStateFlags |=
setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
isAutoTimestamp, postTime, transactionId);
} else /*mLayerLifecycleManagerEnabled*/ {
// ...
}
// ...
}
transactionFlags |= clientStateFlags;
// 2. mInputWindowCommands 合并传入的参数 inputWindowCommands
// inputWindowCommands 保存了焦点窗口请求
transactionFlags |= addInputWindowCommands(inputWindowCommands);
// ...
return needsTraversal;
}
// 处理 layer change
uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
ResolvedComposerState& composerState,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint64_t transactionId) {
// composerState 是要更新的 layer 数据
layer_state_t& s = composerState.state;
// ...
const uint64_t what = s.what;
uint32_t flags = 0;
sp<Layer> layer = nullptr;
if (s.surface) {
// 获取 Layer,下面就开始更新 Layer 信息
layer = LayerHandle::getLayer(s.surface);
} else {
// ...
}
// ..
// 更新窗口信息时,设置过 eInputInfoChanged
if (what & layer_state_t::eInputInfoChanged) {
// 用 layer->mDrawingState.inputInfo 保存窗口信息
layer->setInputInfo(*s.windowInfoHandle->getInfo());
flags |= eTraversalNeeded;
}
// ...
return flags;
}
现在,Layer 使用 mDrawingState.inputInfo 保存了输入窗口的信息,SurfaceFlinger 使用 mInputWindowCommands 保存了焦点窗口请求。现在需要把这些数据同步给 InputFlinger,如下
cpp
// SurfaceFlinger.cpp
void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
ATRACE_CALL();
std::vector<WindowInfo> windowInfos;
std::vector<DisplayInfo> displayInfos;
bool updateWindowInfo = false;
// Layer 保存窗口信息是,会把 mUpdateInputInfo 更新为 true
if (mUpdateInputInfo) {
mUpdateInputInfo = false;
updateWindowInfo = true;
// 把 window info 和 display info 分别填充到 windowInfos 到 displayInfos
buildWindowInfos(windowInfos, displayInfos);
}
std::unordered_set<int32_t> visibleWindowIds;
for (WindowInfo& windowInfo : windowInfos) {
if (!windowInfo.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
visibleWindowIds.insert(windowInfo.id);
}
}
bool visibleWindowsChanged = false;
if (visibleWindowIds != mVisibleWindowIds) {
visibleWindowsChanged = true;
mVisibleWindowIds = std::move(visibleWindowIds);
}
// 后台线程执行 Callback
BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
windowInfos = std::move(windowInfos),
displayInfos = std::move(displayInfos),
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
visibleWindowsChanged, vsyncId, frameTime]() {
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
if (updateWindowInfo) {
// 1. WindowInfosListenerInvoker 处理窗口信息改变
mWindowInfosListenerInvoker
->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
std::move(displayInfos),
vsyncId.value, frameTime.ns()},
std::move(
inputWindowCommands.windowInfosReportedListeners),
/* forceImmediateCall= */ visibleWindowsChanged ||
!inputWindowCommands.focusRequests.empty());
} else {
// ...
}
// 2. 把焦点窗口请求发送给 InputFlinger
for (const auto& focusRequest : inputWindowCommands.focusRequests) {
inputFlinger->setFocusedWindow(focusRequest);
}
}});
mInputWindowCommands.clear();
}
InputDispatcher 更新窗口信息
WindowInfosListenerInvoker 管理所有对窗口感兴趣的监听者,它会把窗口信息发送给给监听者,如下
cpp
// WindowInfosListenerInvoker.cpp
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
// ...
// Call the listeners
for (auto& pair : mWindowInfosListeners) {
auto& [listenerId, listener] = pair.second;
// 把窗口信息发送给 listener
auto status = listener->onWindowInfosChanged(update);
if (!status.isOk()) {
ackWindowInfosReceived(update.vsyncId, listenerId);
}
}
}
InputDispatcher 就注册过一个 listener 如下
cpp
// InputDispatcher.cpp
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::chrono::nanoseconds staleEventTimeout)
// ...
{
// ...
mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
// ..
}
现在看下 InputDispatcher 如何处理这些需要更新的窗口信息
cpp
// InputDispatcher.cpp
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
const gui::WindowInfosUpdate& update) {
mDispatcher.onWindowInfosChanged(update);
}
void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
// handlesPerDisplay 根据 display id,保存需要更新的窗口信息
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
}
{ // acquire lock
std::scoped_lock _l(mLock);
// Ensure that we have an entry created for all existing displays so that if a displayId has
// no windows, we can tell that the windows were removed from the display.
for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
handlesPerDisplay[displayId];
}
// 这是更新 display info
mDisplayInfos.clear();
for (const auto& displayInfo : update.displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
}
// 这是更新 window info
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
if (update.vsyncId < mWindowInfosVsyncId) {
ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
", current update vsync id: %" PRId64,
mWindowInfosVsyncId, update.vsyncId);
}
mWindowInfosVsyncId = update.vsyncId;
}
// 如果有事件,因为无焦点窗口而阻塞,唤醒线程,继续处理事件分发
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
这里直接看下 InputDispatcher 如何处理窗口信息更新
cpp
// InputDispatcher.cpp
void InputDispatcher::setInputWindowsLocked(
const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
// 打开 DEBUG_FOCUS : adb shell setprop log.tag.InputDispatcherFocus DEBUG" ,然后重启
if (DEBUG_FOCUS) {
std::string windowList;
for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
windowList += iwh->getName() + " ";
}
ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
}
// ...
// Copy old handles for release if they are no longer present.
// 从 mWindowHandlesByDisplay 中,根据 display id,获取当前保存的所有窗口信息
// 这些窗口信息,是旧的信息,因为马上要更新了
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
// >..
// 根据 windowInfoHandles 中的窗口信息,更新 mWindowHandlesByDisplay 保存的窗口信息
// 主要,并不是简单的移除和添加,而只是把窗口的数据更新
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
// 此时从 mWindowHandlesByDisplay 获取的就是更新后的窗口信息
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
// 1.FocusResolver 保存更新后所有的窗口信息
// 同时会根据这些窗口信息,以及焦点请求,来更新焦点窗口
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
// 2. 处理焦点窗口改变
if (changes) {
onFocusChangedLocked(*changes);
}
// ...
}
InputDispatcher 收到窗口数据后,更新了 mWindowHandlesByDisplay 保存的数据。然后,把最新的窗口数据发送给 FocusResolver ,让其解析最新的焦点窗口。最后,处理焦点窗口改变,其实就通知各方,焦点窗口改变,其中就包括上层 InputManagerService & WindowManagerService。
FocusResolver 更新焦点窗口
cpp
// FocusResolver.cpp
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
std::string removeFocusReason;
// 从 mFocusRequestByDisplay 根据 display id ,获取 focus request
const std::optional<FocusRequest> request = getFocusRequest(displayId);
// 从 mFocusedWindowTokenByDisplay 根据 display id,获取焦点窗口
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
// Find the next focused token based on the latest FocusRequest. If the requested focus window
// cannot be focused, focus will be removed.
if (request) {
sp<IBinder> requestedFocus = request->token;
sp<WindowInfoHandle> resolvedFocusWindow;
// 1. 根据请求的焦点窗口,以及更新后的窗口信息,解析新的焦点窗口
Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
return std::nullopt;
}
// mLastFocusResultByDisplay 保存更新焦点窗口后的结果
const Focusability previousResult = mLastFocusResultByDisplay[displayId];
mLastFocusResultByDisplay[displayId] = result;
if (result == Focusability::OK) {
LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
"Focused window should be non-null when result is OK!");
// 2. 找到了焦点窗口,用 mFocusedWindowTokenByDisplay 根据 display id 保存最新的焦点窗口
return updateFocusedWindow(displayId,
"Window became focusable. Previous reason: " +
ftl::enum_string(previousResult),
resolvedFocusWindow->getToken(),
resolvedFocusWindow->getName());
}
removeFocusReason = ftl::enum_string(result);
}
// 3. 没找到焦点窗口,从 mFocusedWindowTokenByDisplay 移除焦点窗口数据
// Focused window is no longer focusable and we don't have a suitable focus request to grant.
// Remove focus if needed.
return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
FocusResolver 获取最新的窗口数据后,再结合当前的焦点窗口请求(焦点窗口请求来自于 SurfaceFlinger),解析最新的焦点窗口,如下
cpp
// FocusResolver.cpp
// token 为焦点窗口请求中的焦点窗口
// windows 为所有最新的窗口信息
// outFocusableWindow 保存解析后的焦点窗口
FocusResolver::Focusability FocusResolver::getResolvedFocusWindow(
const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
sp<WindowInfoHandle>& outFocusableWindow) {
sp<IBinder> curFocusCandidate = token;
bool focusedWindowFound = false;
// Keep track of all windows reached to prevent a cyclical transferFocus request.
std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached;
while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) {
tokensReached.emplace(curFocusCandidate);
// 检测 焦点窗口请求中的焦点窗口 是否可以获得焦点
// 原理是必须在 windows 中匹配到窗口,并且可见、可以获得焦点
Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow);
if (result == Focusability::OK) {
LOG_ALWAYS_FATAL_IF(!outFocusableWindow,
"Focused window should be non-null when result is OK!");
focusedWindowFound = true;
// outFocusableWindow has been updated by isTokenFocusable to contain
// the window info for curFocusCandidate. See if we can grant focus
// to the token that it wants to transfer its focus to.
curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget;
}
// If the initial token is not focusable, return early with the failed result.
if (!focusedWindowFound) {
return result;
}
}
return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW;
}
FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
sp<WindowInfoHandle>& outFocusableWindow) {
bool allWindowsAreFocusable = true;
bool windowFound = false;
sp<WindowInfoHandle> visibleWindowHandle = nullptr;
for (const sp<WindowInfoHandle>& window : windows) {
// token 是请求的焦点窗口
// 这里很显然,是从所有窗口信息中,找到 token 对应的窗口
if (window->getToken() != token) {
continue;
}
// 这里表示 token 对应的窗口,是存在于 windows 中
windowFound = true;
// 检测窗口是否可见
if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
// Check if at least a single window is visible.
visibleWindowHandle = window;
}
// 检测窗口是否可以获得焦点
if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
// Check if all windows with the window token are focusable.
allWindowsAreFocusable = false;
break;
}
}
// 没有焦点请求对应得窗口
if (!windowFound) {
return Focusability::NO_WINDOW;
}
// 虽然找到焦点请求对应的窗口,但是它不能获得焦点
if (!allWindowsAreFocusable) {
return Focusability::NOT_FOCUSABLE;
}
// 虽然找到焦点请求对应的窗口,但是它不可见
if (!visibleWindowHandle) {
return Focusability::NOT_VISIBLE;
}
// 找到了焦点窗口
// Only set the outFoundWindow if the window can be focused
outFocusableWindow = visibleWindowHandle;
return Focusability::OK;
}
解析焦点窗口的原理是,根据焦点窗口请求的焦点窗口,在所有窗口信息中进行匹配,如果匹配到了,并且窗口可见、可以获得焦点,那么这个焦点请求就是有效的,并且还能得到焦点窗口。
得到焦点窗口后,FocusResolver 使用 mFocusedWindowTokenByDisplay ,根据 display id 进行保存,否则进行移除。
处理焦点窗口改变
cpp
// InputDispatcher.cpp
void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
// 旧焦点窗口存在
if (changes.oldFocus) {
std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
// 向 inbound queue 中添加一个类型为 FOCUS 的事件
enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
}
}
// 新焦点窗口存在
if (changes.newFocus) {
// 向 inbound queue 中添加一个类型为 FOCUS 事件
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
// 针对 pointer capture 的
disablePointerCaptureForcedLocked();
// 如果焦点窗口的 display 没有改变
if (mFocusedDisplayId == changes.displayId) {
// 发送一个 focus change 命令,通知上层,焦点窗口改变了
sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
}
}
InputDispatcher 处理焦点窗口改变时,为新旧焦点窗口(必须存在),分别向 inbound queue 中添加了一个类型为 FOCUS 事件,如下
cpp
// InputDispatcher.cpp
void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
const std::string& reason) {
// 把 mPendingEvent 回退到 inbound queue 中,因为可以重新发送给新的焦点窗口
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
mInboundQueue.push_front(mPendingEvent);
mPendingEvent = nullptr;
}
std::unique_ptr<FocusEntry> focusEntry =
std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus,
reason);
// 从 mInboundQueue 中,反向查找 type 为 FOCUS 的 events 的 index
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
[](const std::shared_ptr<EventEntry>& event) {
return event->type == EventEntry::Type::FOCUS;
});
// 把 focusEntry 插入到最后一个 type 为 FOCUS 的 event 之后
// Maintain the order of focus events. Insert the entry after all other focus events.
mInboundQueue.insert(it.base(), std::move(focusEntry));
}
这个类型为 FOCUS 的事件,指定发送给旧/新焦点窗口,如下
cpp
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
// InputTarget 保存窗口信息
InputTarget target;
// channel 是旧焦点窗口,或者新焦点窗口
target.inputChannel = channel;
target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
channel->getName();
std::string reason = std::string("reason=").append(entry->reason);
// event log: 例如
// input_focus: [Focus leaving f3b938f com.android.launcher3/com.android.searchlauncher.SearchLauncher (server),reason=NO_WINDOW]
// input_focus: [Focus entering f87d90c com.awesome.helloworld/com.awesome.helloworld.MainActivity (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
// 分发给旧/新焦点窗口
// TODO: 最终效果是什么呢?
dispatchEventLocked(currentTime, entry, {target});
}
但是,目前我还没研究上层收到这个焦点事件后,会有什么效果,如果有小伙伴知道,可以留言告诉我。
另外,如果焦点窗口的 display 没有改变,还会发送一个命令去通知上层,焦点窗口改变了,如下
cpp
// InputDispatcher.cpp
void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
const sp<IBinder>& newToken) {
auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
// 通知策略,焦点窗口改变
mPolicy.notifyFocusChanged(oldToken, newToken);
};
// 发送一个命令
postCommandLocked(std::move(command));
}
执行这个命令,最终会通知到上层,输入系统的焦点窗口,真的改变了
cpp
// com_android_server_input_InputManagerService.cpp
void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
const sp<IBinder>& newToken) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyFocusChanged");
#endif
ATRACE_CALL();
JNIEnv* env = jniEnv();
ScopedLocalFrame localFrame(env);
jobject oldTokenObj = javaObjectForIBinder(env, oldToken);
jobject newTokenObj = javaObjectForIBinder(env, newToken);
// 通知上层 InputManagerService
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
oldTokenObj, newTokenObj);
checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
}
java
// InputManagerService.java
// Native callback
@SuppressWarnings("unused")
private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
// mWindowManagerCallbacks 是由 WMS 注册的
mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken);
}
java
// InputManagerCallback.java
public void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
// 调用 reportFocusChanged()
mService.mH.sendMessage(PooledLambda.obtainMessage(
mService::reportFocusChanged, oldToken, newToken));
}
java
// WindowManagerService.java
void reportFocusChanged(IBinder oldToken, IBinder newToken) {
InputTarget lastTarget;
InputTarget newTarget;
synchronized (mGlobalLock) {
lastTarget = getInputTargetFromToken(oldToken);
newTarget = getInputTargetFromToken(newToken);
if (newTarget == null && lastTarget == null) {
Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
return;
}
// mFocusedInputTarget 代表输入系统中最新的焦点窗口
mFocusedInputTarget = newTarget;
mAccessibilityController.onFocusChanged(lastTarget, newTarget);
// 这个 log,代表 WMS 收到输入系统焦点窗口改变
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
}
// Call WindowState focus change observers
WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null;
if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) {
// 取消 ANR dialog
mAnrController.onFocusChanged(newFocusedWindow);
// 目前没有发现下面两个方法有什么用
newFocusedWindow.reportFocusChangedSerialized(true);
notifyFocusChanged();
}
WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null;
if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) {
lastFocusedWindow.reportFocusChangedSerialized(false);
}
}
InputFlinger 下发焦点窗口请求
在输入系统中,InputFlinger 指的是 InputManager,因为它实现了 IInputFlinger 接口。所以,将由 InputManager 接收焦点窗口请求,如下
cpp
// InputManager.cpp
binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
// 交给 InputDispatcher 处理
mDispatcher->setFocusedWindow(request);
return binder::Status::ok();
}
cpp
// InputDispatcher.cpp
void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
{ // acquire lock
std::scoped_lock _l(mLock);
// 1. FocusResolver 处理焦点窗口请求
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
if (changes) {
// 2. 处理焦点窗口改变
onFocusChangedLocked(*changes);
}
} // release lock
// 如果有事件因为没有焦点窗口而阻塞,此时唤醒线程就可以处理
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
第二步的处理焦点窗口改变,前面已经分析过,这里主要看下 FocusResolver 如何处理焦点窗口请求
cpp
// FocusResolver.cpp
std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
const int32_t displayId = request.displayId;
// 从 mFocusedWindowTokenByDisplay,根据 display id 获取焦点窗口
// 这是旧焦点窗口请求
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus == request.token) {
ALOGD_IF(DEBUG_FOCUS,
"setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
request.windowName.c_str(), displayId);
return std::nullopt;
}
sp<WindowInfoHandle> resolvedFocusWindow;
// 根据最新的焦点窗口请求,解析焦点窗口
Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
// Update focus request. The focus resolver will always try to handle this request if there is
// no focused window on the display.
mFocusRequestByDisplay[displayId] = request;
mLastFocusResultByDisplay[displayId] = result;
// 如果成功解析出焦点窗口,用 mFocusedWindowTokenByDisplay 以 display id 为 KEY,保存焦点窗口
if (result == Focusability::OK) {
LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
"Focused window should be non-null when result is OK!");
return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(),
resolvedFocusWindow->getName());
}
// 如果没有解析出焦点窗口,从 mFocusedWindowTokenByDisplay 移除焦点窗口
return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
nullptr);
}
根据焦点窗口请求,来解析焦点窗口,前面已经分析过。得到焦点窗口后,根据 display id 保存到 mFocusedWindowTokenByDisplay 中。
更新焦点 app
无焦点窗口 ANR ,除了与交焦点窗口有关,还与焦点 app (实际为 ActivityRecord)有关。
当服务端通知 app 端去 resume activity 后,把这个 activity 作为焦点 app,发送给底层,如下
java
// DisplayContent.java
// newFocus 为焦点 app
boolean setFocusedApp(ActivityRecord newFocus) {
if (newFocus != null) {
final DisplayContent appDisplay = newFocus.getDisplayContent();
if (appDisplay != this) {
throw new IllegalStateException(newFocus + " is not on " + getName()
+ " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
}
// Called even if the focused app is not changed in case the app is moved to a different
// TaskDisplayArea.
onLastFocusedTaskDisplayAreaChanged(newFocus.getDisplayArea());
}
// 焦点 app 必须改变
if (mFocusedApp == newFocus) {
return false;
}
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s",
newFocus, getDisplayId(), Debug.getCallers(4));
final Task oldTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
final Task newTask = newFocus != null ? newFocus.getTask() : null;
// DisplayContent 使用 mFocusedApp 保存焦点 app
mFocusedApp = newFocus;
if (oldTask != newTask) {
if (oldTask != null) oldTask.onAppFocusChanged(false);
if (newTask != null) newTask.onAppFocusChanged(true);
}
// 设置焦点 app
getInputMonitor().setFocusedAppLw(newFocus);
return true;
}
void setFocusedAppLw(ActivityRecord newApp) {
// 向输入系统设置焦点 app
mService.mInputManager.setFocusedApplication(mDisplayId,
newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
}
java
// InputManagerService.java
public void setFocusedApplication(int displayId, InputApplicationHandle application) {
mNative.setFocusedApplication(displayId, application);
}
cpp
// com_android_server_input_InputManagerService.cpp
void NativeInputManager::setFocusedApplication(JNIEnv* env, ui::LogicalDisplayId displayId,
jobject applicationHandleObj) {
if (!applicationHandleObj) {
return;
}
// 转化为 native 层的 InputApplicationHandle
std::shared_ptr<InputApplicationHandle> applicationHandle =
android_view_InputApplicationHandle_getHandle(env, applicationHandleObj);
applicationHandle->updateInfo();
// 焦点 app 直接发送给 InputDispatcher
mInputManager->getDispatcher().setFocusedApplication(displayId, applicationHandle);
}
cpp
// InputDispatcher.cpp
void InputDispatcher::setFocusedApplication(
int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
// 执行命令 adb shell setprop log.tag.InputDispatcherFocus DEBUG,然后重启
// 就可以打开 DEBUG_FOCUS 开关
if (DEBUG_FOCUS) {
ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
}
{ // acquire lock
std::scoped_lock _l(mLock);
// 设置焦点 app
setFocusedApplicationLocked(displayId, inputApplicationHandle);
} // release lock
// 唤醒线程
// 因为有的事件还在等待焦点 app,才能分发
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
void InputDispatcher::setFocusedApplicationLocked(
int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
// 从 mFocusedApplicationHandlesByDisplay 根据 display id 获取焦点 app
std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
// 这里是检测焦点app是否改变
if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
return; // This application is already focused. No need to wake up or change anything.
}
if (inputApplicationHandle != nullptr) {
// mFocusedApplicationHandlesByDisplay 根据 display id 保存焦点 app
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
} else {
// ...
}
// 如果有事件正在等待一个旧的焦点 app,而此时焦点 app 改变,因此需要重置
// No matter what the old focused application was, stop waiting on it because it is
// no longer focused.
resetNoFocusedWindowTimeoutLocked();
}
焦点app,最终由 InputDispater 的 mFocusedApplicationHandlesByDisplay,以 display id 为 KEY 进行保存。