以下分析基于Android S.
简述 上文中,我们知道了应用View和窗口与input系统交互通道InputChannel的打通过程。有了通信通道,就可以通过这个来通信,将input事件传递给应用程序。很自然的,input系统中必须要保存代表该应用窗口的对象,用于识别以及分发事件。
还记得我们初始化WindowState时,有创建过一个InputWindowHandleWrapper类的对象,当时我们认为是将该Window注册进input系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mInputWindowHandle = new InputWindowHandleWrapper (new InputWindowHandle ( mActivityRecord != null ? mActivityRecord.getInputApplicationHandle(false ) : null , getDisplayId())); @NonNull InputApplicationHandle getInputApplicationHandle (boolean update) { if (mInputApplicationHandle == null ) { mInputApplicationHandle = new InputApplicationHandle (appToken, toString(), mInputDispatchingTimeoutMillis); ...... return mInputApplicationHandle; }
我们先看看这里InputWindowHandleWrapper、InputWindowHandle、InputApplicationHandle等相关类的类图:
可以看到这里InputWindowHandle类中包含该Window的大小和位置、可触碰区域(touchableRegion)等等信息,这些信息是什么时候更新的呢?回到我们之前研究过的焦点窗口的更新一文,在WMS.addWindow中创建WindowState的对象并且更新焦点窗口后,会更新input相关信息:
1 2 3 4 5 6 7 8 9 if (focusChanged) { displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, false ); } displayContent.getInputMonitor().updateInputWindowsLw(false );
这里我们加上mCurrentFocus就是此次新创建的WindowState.
一. 更新窗口信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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 ; } setUpdateInputWindowsNeededLw(); if (updateInputWindows) { updateInputWindowsLw(false ); } } void setUpdateInputWindowsNeededLw () { mUpdateInputWindowsNeeded = true ; }
这里是判断新的窗口是否与当前输入焦点窗口一致,如果不一致,则标记mUpdateInputWindowsNeeded为true,表明需要更新输入窗口了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void updateInputWindowsLw (boolean force) { if (!force && !mUpdateInputWindowsNeeded) { return ; } scheduleUpdateInputWindows(); } private void scheduleUpdateInputWindows () { if (mDisplayRemoved) { return ; } if (!mUpdateInputWindowsPending) { mUpdateInputWindowsPending = true ; mHandler.post(mUpdateInputWindows); } }
先判断是否需要执行更新(mUpdateInputWindowsNeeded变量),如果需要则将更新操作交给”android.anim”线程处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void run () { synchronized (mService.mGlobalLock) { mUpdateInputWindowsPending = false ; mUpdateInputWindowsNeeded = false ; if (mDisplayRemoved) { return ; } final boolean inDrag = mService.mDragDropController.dragDropActiveLocked(); mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag); } }
在”android.anim”线程处理时,首先将标记变量恢复为false, 表示可以接收下一次输入窗口更新了。接下来就是在默认Display中添加所有窗口了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private void updateInputWindows (boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows" ); mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); mAddPipInputConsumerHandle = mPipInputConsumer != null ; mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null ; mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null ; mDisableWallpaperTouchEvents = false ; mInDrag = inDrag; resetInputConsumers(mInputTransaction); mRecentsAnimationFocusOverride = false ; mDisplayContent.forAllWindows(this , true ); if (mRecentsAnimationFocusOverride) { requestFocus(mRecentsAnimationInputConsumer.mWindowHandle.token, mRecentsAnimationInputConsumer.mName); } else { updateInputFocusRequest(); } if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } InputConsumerImpl getInputConsumer (String name) { return mInputConsumers.get(name); }
InputConsumer总共有四种:
INPUT_CONSUMER_PIP: “pip_input_consumer”, 用于pip
INPUT_CONSUMER_NAVIGATION: “nav_input_consumer”,用于导航栏
INPUT_CONSUMER_WALLPAPER: “wallpaper_input_consumer”,用于壁纸
INPUT_CONSUMER_RECENTS_ANIMATION: “recents_animation_input_consumer”,用于多任务
更新输入窗口的步骤如下:
重置InputConsumer,将所有consumer都调用hide
从上到下(Z轴大到小)遍历该DisplayContent中所有的WindowState,依次执行UpdateInputForAllWindowsConsumer.accept
这个accept就是计算更新窗口信息,比如可触碰区域的计算
通过SurfaceControl传递窗口信息给SurfaceFlinger,在native层生成对应的InputWindowHandle
如果有最近任务栏动画,则调用requestFocus更新多任务焦点;否则调用updateInputFocusRequest更新输入焦点请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void accept (WindowState w) { final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; ...... mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken, w.getKeyInterceptionInfo()); if (w.mWinAnimator.hasSurface()) { populateInputWindowHandle(inputWindowHandle, w); setInputWindowInfoIfNeeded(mInputTransaction, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); } }
1.5.1 WindowStateAnimator.hasSurface 1 2 3 4 5 6 7 8 9 10 11 boolean hasSurface () { return mSurfaceController != null && mSurfaceController.hasSurface(); } boolean hasSurface () { return mSurfaceControl != null ; }
WindowStateAnimator中的mSurfaceController是WindowSurfaceController的对象,在其对应的Window被调用relayoutWindow时通过winAnimator.createSurfaceLocked(win.mAttrs.type)创建的。 而relayoutWindow则是三方应用进程接收到Vsync信号之后,调用对应的ViewRootImpl中的performTraversals在通过Session通知到WMS执行的。在这里我们假设”android.anim”线程更新所有输入窗口时,这个新建的WindowState已经被调用过了relayoutWindow,存在Surface。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 void populateInputWindowHandle (final InputWindowHandleWrapper inputWindowHandle, final WindowState w) { inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null ? w.mActivityRecord.getInputApplicationHandle(false ) : null ); inputWindowHandle.setToken(w.mInputChannelToken); inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis()); inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode()); inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures); inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused); inputWindowHandle.setVisible(w.isVisible()); final boolean focusable = w.canReceiveKeys() && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop()); inputWindowHandle.setFocusable(focusable); final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w) && !mService.mPolicy.isKeyguardShowing() && !mDisableWallpaperTouchEvents; inputWindowHandle.setHasWallpaper(hasWallpaper); final Rect frame = w.getFrame(); inputWindowHandle.setFrame(frame.left, frame.top, frame.right, frame.bottom); inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left); inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f ); final int flags = w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs.flags); inputWindowHandle.setTouchableRegion(mTmpRegion); inputWindowHandle.setLayoutParamsFlags(flags); ...... }
1.5.2.1 WindowState.getSurfaceTouchableRegion 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int getSurfaceTouchableRegion (Region region, int flags) { final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0 ; if (modal) { flags |= FLAG_NOT_TOUCH_MODAL; if (mActivityRecord != null ) { updateRegionForModalActivityWindow(region); } else { getDisplayContent().getBounds(mTmpRect); final int dw = mTmpRect.width(); final int dh = mTmpRect.height(); region.set(-dw, -dh, dw + dw, dh + dh); } subtractTouchExcludeRegionIfNeeded(region); } else { getTouchableRegion(region); } final Rect frame = mWindowFrames.mFrame; if (frame.left != 0 || frame.top != 0 ) { region.translate(-frame.left, -frame.top); } ...... return flags; }
FLAG_NOT_TOUCH_MODAL: 允许窗口外的任何指针事件被发送到它后面的窗口, 即使这个窗口是可聚焦的。否则(不带此标志),窗口将消耗所有指针事件本身,而不管它们是否在窗口内。
FLAG_NOT_FOCUSABLE: 标记这个窗口永远不能接收触摸事件
Android中坐标原点在左上角,Y轴正方向向下,X轴正方向向右,所以计算的窗口可触摸区域需要转换为基于Surface的坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void updateRegionForModalActivityWindow (Region outRegion) { mActivityRecord.getLetterboxInnerBounds(mTmpRect); if (mTmpRect.isEmpty()) { final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds(); if (transformedBounds != null ) { mTmpRect.set(transformedBounds); } else { final Task task = getTask(); if (task != null ) { task.getDimBounds(mTmpRect); } else if (getRootTask() != null ) { getRootTask().getDimBounds(mTmpRect); } } } adjustRegionInFreefromWindowMode(mTmpRect); outRegion.set(mTmpRect); cropRegionToRootTaskBoundsIfNeeded(outRegion); }
ActivityRecord.getLetterboxInnerBounds是获取内部边界的letterbox可触摸区域;一般不会设置的,这个就是中的android:maxAspectRatio属性。
至于freeform模式的窗口调整区域大小以及根据坐标系调整RootTask边界,感兴趣的可以继续研究。这里仅须知道窗口的可触摸区域是怎么拿到的即可。
1 2 3 4 5 6 7 8 9 10 11 Rect getFixedRotationTransformDisplayBounds () { return isFixedRotationTransforming() ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration .getBounds() : null ; } boolean isFixedRotationTransforming () { return mFixedRotationTransformState != null && mFixedRotationTransformState.mIsTransforming; }
mFixedRotationTransformState是当手机方向旋转之后就会生成的, 这里我们假设没有发生旋转。
1.5.2.4 Task.getDimBounds 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void getDimBounds (Rect out) { if (isRootTask()) { getBounds(out); return ; } final Task rootTask = getRootTask(); final DisplayContent displayContent = rootTask.getDisplayContent(); final boolean dockedResizing = displayContent != null && displayContent.mDividerControllerLocked.isResizing(); if (inFreeformWindowingMode()) { boolean [] foundTop = { false }; final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds, PooledLambda.__(ActivityRecord.class), out, foundTop); forAllActivities(c); c.recycle(); if (foundTop[0 ]) { return ; } } if (!matchParentBounds()) { if (dockedResizing) { rootTask.getBounds(out); } else { rootTask.getBounds(mTmpRect); mTmpRect.intersect(getBounds()); out.set(mTmpRect); } } else { out.set(getBounds()); } return ; }
窗口模式有如下7种:
窗口模式
值
含义
WINDOWING_MODE_UNDEFINED
0
当前窗口模式尚未定义
WINDOWING_MODE_FULLSCREEN
1
占据屏幕或父容器的整个区域
WINDOWING_MODE_PINNED
2
总是在顶部(总是可见, 覆盖它的父容器中的其他兄弟容器)
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
3
驱动屏幕处于分屏模式的主容器
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
4
在分屏模式下,紧邻WINDOWING_MODE_SPLIT_SCREEN_PRIMARY容器的容器
WINDOWING_MODE_FREEFORM
5
可以在其父容器内自由调整大小,如悬浮窗
WINDOWING_MODE_MULTI_WINDOW
6
窗口管理器中没有表示属性的通用多窗口
获取Task的边界大小的过程如下:
如果当前Task就是RootTask, 那么直接返回该Task的边界
判断该Task的WindowMode是否是Freeform模式,如果是,则找到该Task中最大的可见的区域并返回
当前Task和父容器边界不匹配时
如果该Task被最小化时,直接返回该Task的RootTask的边界
否则获取RootTask边界与当前Task边界的交集并返回
当前Task和父容器边界匹配时
直接返回该Task的边界
1 2 3 4 5 6 7 8 9 10 11 static void setInputWindowInfoIfNeeded (SurfaceControl.Transaction t, SurfaceControl sc, InputWindowHandleWrapper inputWindowHandle) { if (DEBUG_INPUT) { Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle); } if (inputWindowHandle.isChanged()) { inputWindowHandle.applyChangesToSurface(t, sc); } }
1 2 3 4 5 void applyChangesToSurface (@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) { t.setInputWindowInfo(sc, mHandle); mChanged = false ; }
1 2 3 4 5 6 7 8 9 10 public Transaction setInputWindowInfo (SurfaceControl sc, InputWindowHandle handle) { checkPreconditions(sc); nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle); return this ; } private static native void nativeSetInputWindowInfo (long transactionObj, long nativeObject, InputWindowHandle handle) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 ()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle ( JNIEnv* env, jobject inputWindowHandleObj) { if (!inputWindowHandleObj) { return NULL ; } AutoMutex _l(gHandleMutex); jlong ptr = env->GetLongField (inputWindowHandleObj, gInputWindowHandleClassInfo.ptr); NativeInputWindowHandle* handle; if (ptr) { handle = reinterpret_cast <NativeInputWindowHandle*>(ptr); } else { jweak objWeak = env->NewWeakGlobalRef (inputWindowHandleObj); handle = new NativeInputWindowHandle (objWeak); handle->incStrong ((void *)android_view_InputWindowHandle_getHandle); env->SetLongField (inputWindowHandleObj, gInputWindowHandleClassInfo.ptr, reinterpret_cast <jlong>(handle)); } return handle; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool NativeInputWindowHandle::updateInfo () { ...... mInfo.touchableRegion.clear (); ...... jobject regionObj = env->GetObjectField (obj, gInputWindowHandleClassInfo.touchableRegion); if (regionObj) { for (graphics::RegionIterator it (env, regionObj); !it.isDone (); it.next ()) { ARect rect = it.getRect (); mInfo.addTouchableRegion (Rect (rect.left, rect.top, rect.right, rect.bottom)); } env->DeleteLocalRef (regionObj); } ...... }
这里就是将java层InputWindowHandle里的信息同步给NativeInputWindowHandle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo ( const sp<SurfaceControl>& sc, const InputWindowInfo& info) { layer_state_t * s = getLayerState (sc); if (!s) { mStatus = BAD_INDEX; return *this ; } s->inputHandle = new InputWindowHandle (info); s->what |= layer_state_t ::eInputInfoChanged; return *this ; }
这里是将窗口相关信息存入了SurfaceFling中,至于怎么传输的,为什么需要这些信息,我们后续研究WMS的窗口和SurfaceFlinger的关系时分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private void updateInputFocusRequest () { final WindowState focus = mDisplayContent.mCurrentFocus; final IBinder focusToken = focus != null ? focus.mInputChannelToken : null ; ...... requestFocus(focusToken, focus.getName()); } private void requestFocus (IBinder focusToken, String windowName) { ...... mInputFocus = focusToken; mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName, "reason=UpdateInputWindows" ); ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s" , windowName); }
注意mInputTransaction其实还是SurfaceControl.Transaction类, 在InputMonitor初始化时创建的:
mInputTransaction = mService.mTransactionFactory.get();
1.6.1 SurfaceControl.setFocusedWindow 1 2 3 4 5 6 7 8 9 public Transaction setFocusedWindow (@NonNull IBinder token, String windowName, int displayId) { nativeSetFocusedWindow(mNativeObject, token, windowName, null , null , displayId); return this ; } private static native void nativeSetFocusedWindow (long transactionObj, IBinder toToken, String windowName, IBinder focusedToken, String focusedWindowName, int displayId) ;
通过JNI调用到native层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 static void nativeSetFocusedWindow (JNIEnv* env, jclass clazz, jlong transactionObj, jobject toTokenObj, jstring windowNameJstr, jobject focusedTokenObj, jstring focusedWindowNameJstr, jint displayId) { auto transaction = reinterpret_cast <SurfaceComposerClient::Transaction*>(transactionObj); if (toTokenObj == NULL ) return ; sp<IBinder> toToken (ibinderForJavaObject(env, toTokenObj)) ; ...... FocusRequest request; request.token = toToken; ...... transaction->setFocusedWindow (request); } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow ( const FocusRequest& request) { mInputWindowCommands.focusRequests.push_back (request); return *this ; }
将传入的焦点窗口的mInputChannelToken和其他信息打包封装在FocusRequest中,存入SurfaceComposerClient的mInputWindowCommands.focusRequests集合中。
那么这个mInputWindowCommands.focusRequests是什么时候使用的呢,当调用SurfaceControl.Transaction.apply()函数时,会通过binder将该Transaction的所有信息传递给SurfaceFlinger进程(sf->setTransactionState接口),SurfaceFlinger接收到该Transaction后将其保存在mTransactionQueue队列中。然后在下一次Vsync信号来临时,即onMessageInvalidate函数中,将Transaction从mTransactionQeue中提取出来存入mPendingTransactionQueues队列中,于此同时调用addInputWindowCommands将该Transaction中的inputWindowHandles保存在SurfaceFlinger的mInputWindowCommands中,之后就调用updateInputFlinger()将mInputWindowCommands中的focusRequests更新到InputFlinger中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void SurfaceFlinger::updateInputFlinger () { ATRACE_CALL (); if (!mInputFlinger) { return ; } if (mVisibleRegionsDirty || mInputInfoChanged) { mInputInfoChanged = false ; updateInputWindowInfo (); } else if (mInputWindowCommands.syncInputWindows) { setInputWindowsFinished (); } for (const auto & focusRequest : mInputWindowCommands.focusRequests) { mInputFlinger->setFocusedWindow (focusRequest); } mInputWindowCommands.clear (); }
上面我们分析了窗口的信息的收集过程,重点是可触碰区域的计算,现在我们分析一下窗口信息传递给InputFlinger的过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void SurfaceFlinger::updateInputWindowInfo () { std::vector<InputWindowInfo> inputInfos; mDrawingState.traverseInReverseZOrder ([&](Layer* layer) { ...... inputInfos.push_back (layer->fillInputInfo (display)); }); mInputFlinger->setInputWindows (inputInfos, mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener : nullptr ); }
这里是遍历所有的需要更新的Layer, 依次计算该Layer对应的窗口可触摸区域,将结果保存在InputWindowInfo的Vector中,然后通过Binder传给InputFlinger进程。
注意这里对layer的遍历是沿着Z轴反方向的,也就是从上到下的遍历顺序。layer在上面,存入inputInfos队列前面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 InputWindowInfo Layer::fillInputInfo (const sp<DisplayDevice>& display) { ...... InputWindowInfo info = mDrawingState.inputInfo; ...... fillInputFrameInfo (info, toPhysicalDisplay); info.visible = hasInputInfo () ? canReceiveInput () : isVisible (); info.alpha = getAlpha (); ...... return info; }
根据显示屏再次计算可触摸区域以及其他相关信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void Layer::fillInputFrameInfo (InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) { Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE ? getInputBounds () : info.touchableRegion.getBounds (); ...... ui::Transform layerToDisplay = getInputTransform (); ui::Transform t = toPhysicalDisplay * layerToDisplay; ...... ui::Transform inverseTransform = t.inverse (); Rect nonTransformedBounds = inverseTransform.transform (transformedLayerBounds); vec2 translation = t.transform (nonTransformedBounds.left, nonTransformedBounds.top); ui::Transform inputTransform (t) ; inputTransform.set (translation.x, translation.y); info.transform = inputTransform.inverse (); transformedLayerBounds.intersect (screenBounds, &transformedLayerBounds); info.frameLeft = transformedLayerBounds.left; info.frameTop = transformedLayerBounds.top; info.frameRight = transformedLayerBounds.right; info.frameBottom = transformedLayerBounds.bottom; info.touchableRegion = inputTransform.transform (info.touchableRegion); }
虽然我们在WMS中有计算过可触摸区域,但通过SurfaceFlinger还是需要更加Display实际大小等加工一下,确定最终窗口的可触摸区域。
这里的计算过程就不展开分析了,感兴趣的可以自行研究。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 binder::Status InputManager::setInputWindows ( const std::vector<InputWindowInfo>& infos, const sp<ISetInputWindowsListener>& setInputWindowsListener) { std::unordered_map<int32_t , std::vector<sp<InputWindowHandle>>> handlesPerDisplay; std::vector<sp<InputWindowHandle>> handles; for (const auto & info : infos) { handlesPerDisplay.emplace (info.displayId, std::vector<sp<InputWindowHandle>>()); handlesPerDisplay[info.displayId].push_back (new BinderWindowHandle (info)); } mDispatcher->setInputWindows (handlesPerDisplay); if (setInputWindowsListener) { setInputWindowsListener->onSetInputWindowsFinished (); } return binder::Status::ok (); }
BinderWindowHandle就是个包装类,继承了InputWindowHandle,并且重写了updateInfo, 内部只有一个InputWindowHandle的成员变量。这是为了避免后续不小心更新窗口信息。
这里将从SF传过来的InputWindowInfo信息打包成BinderWindowHandle,存入对应DisplayId的InputWindowHandle集合中,然后交给InputDispatcher处理。
1 2 3 4 5 6 7 8 9 10 11 void InputDispatcher::setInputWindows ( const std::unordered_map<int32_t , std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) { { std::scoped_lock _l(mLock); for (const auto & [displayId, handles] : handlesPerDisplay) { setInputWindowsLocked (handles, displayId); } } mLooper->wake (); }
针对所有的Display依次更新输入窗口信息, 然后唤醒轮询循环,做出新的输入分派选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void InputDispatcher::setInputWindowsLocked ( const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { ...... const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked (displayId); updateWindowHandlesForDisplayLocked (inputWindowHandles, displayId); ...... std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setInputWindows (displayId, windowHandles); ...... for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { if (getWindowHandleLocked (oldWindowHandle) == nullptr ) { ...... oldWindowHandle->releaseChannel (); ...... } } } void InputDispatcher::updateWindowHandlesForDisplayLocked ( const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { ...... std::vector<sp<InputWindowHandle>> newHandles; for (const sp<InputWindowHandle>& handle : inputWindowHandles) { ...... if ((oldHandlesById.find (handle->getId ()) != oldHandlesById.end ()) && (oldHandlesById.at (handle->getId ())->getToken () == handle->getToken ())) { const sp<InputWindowHandle>& oldHandle = oldHandlesById.at (handle->getId ()); oldHandle->updateFrom (handle); newHandles.push_back (oldHandle); } else { newHandles.push_back (handle); } } mWindowHandlesByDisplay[displayId] = newHandles; }
这里是更新窗口输入信息至mWindowHandlesByDisplay,将不在展示的窗口信息释放掉,节省资源。
SurfaceFlinger通过binder将焦点窗口同步更新给InputFlinger。
1 2 3 4 5 binder::Status InputManager::setFocusedWindow (const FocusRequest& request) { mDispatcher->setFocusedWindow (request); return binder::Status::ok (); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void InputDispatcher::setFocusedWindow (const FocusRequest& request) { { std::scoped_lock _l(mLock); std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setFocusedWindow (request, getWindowHandlesLocked (request.displayId)); if (changes) { onFocusChangedLocked (*changes); } } mLooper->wake (); }
3.3 FocusResolver.setFocusedWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow ( const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) { const int32_t displayId = request.displayId; 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 ; } if (request.focusedToken) { ....... } Focusability result = isTokenFocusable (request.token, windows); mFocusRequestByDisplay[displayId] = request; mLastFocusResultByDisplay[displayId] = result; if (result == Focusability::OK) { return updateFocusedWindow (displayId, "setFocusedWindow" , request.token, request.windowName); } return updateFocusedWindow (displayId, "Waiting for window because " + NamedEnum::string (result), nullptr ); }
如果当前焦点窗口就是需要设置的窗口,直接返回。然后检查传入的待聚焦的窗口是否可聚焦且在windows列表中, 更新mFocusRequestByDisplay和mLastFocusResultByDisplay,最后无论待更新的焦点窗口是否可聚焦,都更新焦点窗口,即将焦点窗口token(对应InputChannel的token)保存在mFocusedWindowTokenByDisplay中。
3.4 FocusResolver.updateFocusedWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow ( int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus, const std::string& tokenName) { sp<IBinder> oldFocus = getFocusedWindowToken (displayId); if (newFocus == oldFocus) { return std::nullopt ; } if (newFocus) { mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus}; } else { mFocusedWindowTokenByDisplay.erase (displayId); } return {{oldFocus, newFocus, displayId, reason}}; }
将焦点窗口token(对应InputChannel的token,即InputChannel初始化时创建的BBinder)保存在mFocusedWindowTokenByDisplay中。
小结 总的来说,WMS上层创建WindowState之后,如果该窗口可以接收input事件就需要更新焦点窗口,其后更新输入窗口信息给Input系统。注意这里涉及了三个模块:System_server(WMS),InputFlinger和SurfaceFlinger。之所以需要SurfaceFlinger,一是需要借助SurfaceControl通道通信,更重要的是,需要通过SurfaceFlinger进一步去计算窗口的信息,如可触摸区域、可见区域等等。在SurfaceFlinger计算完毕后,通过Binder调用将窗口信息封装成InputWindowInfo传给InputFlinger,InputFlinger将传过来的InputWindowInfo信息打包成BinderWindowHandle存入mWindowHandlesByDisplay中。
接下来我们看看一次触摸事件分发给窗口的流程作为Input事件和Window的结束语。