8295智能座舱弹窗点击问题,点击window之外的区域,window不消失的问题。touchableRegion的问题分析(android 13)
1.问题描述
在项目开发过程中,遇到input的问题。用户点击status bar的Wifi图标之后,会弹出wifi列表的window,而点击这个window之外的区域,wifi列表的窗口不会消失的问题。
2. 问题分析定位
分析触摸问题,必不可少的会用到下面的两个dump命令,用来查询当前window的信息,以及window对应的inputManager的信息。
adb shell dumpsys window w > ws.log
adb shell dumpsys input > input.log
2: name='a0f96b1 com.android.carsettings', id=265, displayId=0, inputConfig=PREVENT_SPLITTING | TRUSTED_OVERLAY | WATCH_OUTSIDE_TOUCH, alpha=1.00, frame=[1677,127][2397,847], globalScale=1.000000, applicationInfo.name=, applicationInfo.token=<null>, touchableRegion=[-2560,-1440][5120,2880], ownerPid=11831, ownerUid=1000, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTED
transform (ROT_0) (TRANSLATE)
1.0000 0.0000 -1677.0000
0.0000 1.0000 -127.0000
0.0000 0.0000 1.0000
从input info可以看出来Wifi列表的弹框对应的
window:‘a0f96b1 com.android.carsettings’
frame,也就是window的位置和大小:frame=[1677,127][2397,847]
touchableRegion,也就是触摸区域:touchableRegion=[-2560,-1440][5120,2880]
代码跟踪:
@VisibleForTesting
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()
&& (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
inputWindowHandle.setFocusable(focusable);
final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w)
&& !mService.mPolicy.isKeyguardShowing()
&& !mDisableWallpaperTouchEvents;
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) {
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
// be applied using the SurfaceControl hierarchy from the Organizer. This means
// we need to make sure that these changes in crop are reflected in the input
// windows, and so ensure this flag is set so that the input crop always reflects
// the surface hierarchy.
useSurfaceBoundsAsTouchRegion = true;
if (w.mAttrs.isModal()) {
TaskFragment parent = w.getTaskFragment();
touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null;
}
} else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
touchableRegionCrop = task.getRootTask().getSurfaceControl();
}
}
inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
if (!useSurfaceBoundsAsTouchRegion) {
Slog.w("InputWindow", "setTouchableRegion, mTmpRegion = " + mTmpRegion + ", w = " + w);
w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
inputWindowHandle.setTouchableRegion(mTmpRegion);
}
}
接下来看看WindowState计算touchableRegion的过程:
void getSurfaceTouchableRegion(Region region, WindowManager.LayoutParams attrs) {
final boolean modal = attrs.isModal();
Slog.w("InputWindow", "getSurfaceTouchableRegion, region = " + region + ", attrs= " + attrs + ", this = " + this
+ ", modal = " + modal + ", mTouchableInsets = " + mTouchableInsets + ", mFrame = " + mWindowFrames.mFrame
+ ", mWindowFrames = " + mWindowFrames);
if (modal) {
if (mActivityRecord != null) {
// Limit the outer touch to the activity root task region.
updateRegionForModalActivityWindow(region);
Slog.w("InputWindow", "getSurfaceTouchableRegion-1, region = " + region + ", attrs= " + attrs + ", this = " + this);
} else {
// Give it a large touchable region at first because it was touch modal. The window
// might be moved on the display, so the touchable region should be large enough to
// ensure it covers the whole display, no matter where it is moved.
getDisplayContent().getBounds(mTmpRect);
final int dw = mTmpRect.width();
final int dh = mTmpRect.height();
region.set(-dw, -dh, dw + dw, dh + dh);
Slog.w("InputWindow", "getSurfaceTouchableRegion-2, region = " + region + ", attrs= " + attrs + ", this = " + this);
}
subtractTouchExcludeRegionIfNeeded(region);
Slog.w("InputWindow", "getSurfaceTouchableRegion-3, region = " + region + ", attrs= " + attrs + ", this = " + this);
} else {
// Not modal
getTouchableRegion(region);
Slog.w("InputWindow", "getSurfaceTouchableRegion-4, region = " + region + ", attrs= " + attrs + ", this = " + this);
}
// Translate to surface based coordinates.
final Rect frame = mWindowFrames.mFrame;
if (frame.left != 0 || frame.top != 0) {
region.translate(-frame.left, -frame.top);
Slog.w("InputWindow", "getSurfaceTouchableRegion-5, region = " + region + ", attrs= " + attrs + ", this = " + this);
}
if (modal && mTouchableInsets == TOUCHABLE_INSETS_REGION) {
// The client gave us a touchable region and so first
// we calculate the untouchable region, then punch that out of our
// expanded modal region.
mTmpRegion.set(0, 0, frame.right, frame.bottom);
mTmpRegion.op(mGivenTouchableRegion, Region.Op.DIFFERENCE);
region.op(mTmpRegion, Region.Op.DIFFERENCE);
Slog.w("InputWindow", "getSurfaceTouchableRegion-6, region = " + region + ", attrs= " + attrs + ", this = " + this);
}
// TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
// scaling but the existing logic doesn't expect that. The result is that the already-
// scaled region ends up getting sent to surfaceflinger which then applies the scale
// (again). Until this is resolved, apply an inverse-scale here.
if (mInvGlobalScale != 1.f) {
region.scale(mInvGlobalScale);
}
Slog.w("InputWindow", "getSurfaceTouchableRegion, region = " + region + ", attrs= " + attrs + ", this = " + this
+ ", mInvGlobalScale = " + mInvGlobalScale);
}
log如下:
01-01 16:00:07.572 1704 1729 W InputWindow: setTouchableRegion, mTmpRegion = SkRegion((0,0,2560,1440)), w = Window{7542c78 u0 com.android.carsettings}
01-01 16:00:07.572 1704 1729 W InputWindow: getSurfaceTouchableRegion, region = SkRegion((0,0,2560,1440)), attrs= {(163,127)(720x720) gr=TOP END CENTER sim={adjust=resize} ty=NAVIGATION_BAR_PANEL fmt=RGBA_8888
01-01 16:00:07.572 1704 1729 W InputWindow: fl=LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS WATCH_OUTSIDE_TOUCH HARDWARE_ACCELERATED
01-01 16:00:07.572 1704 1729 W InputWindow: pfl=USE_BLAST
01-01 16:00:07.572 1704 1729 W InputWindow: bhv=DEFAULT
01-01 16:00:07.572 1704 1729 W InputWindow: fitTypes=NAVIGATION_BARS CAPTION_BAR}, this = Window{7542c78 u0 com.android.carsettings}, modal = true, mTouchableInsets = 0, mFrame = Rect(1677, 127 - 2397, 847), mWindowFrames = com.android.server.wm.WindowFrames@e67a3a8
01-01 16:00:07.573 1704 1729 W InputWindow: getSurfaceTouchableRegion-2, region = SkRegion((-2560,-1440,5120,2880)), attrs= {(163,127)(720x720) gr=TOP END CENTER sim={adjust=resize} ty=NAVIGATION_BAR_PANEL fmt=RGBA_8888
01-01 16:00:07.573 1704 1729 W InputWindow: fl=LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS WATCH_OUTSIDE_TOUCH HARDWARE_ACCELERATED
01-01 16:00:07.573 1704 1729 W InputWindow: pfl=USE_BLAST
01-01 16:00:07.573 1704 1729 W InputWindow: bhv=DEFAULT
01-01 16:00:07.573 1704 1729 W InputWindow: fitTypes=NAVIGATION_BARS CAPTION_BAR}, this = Window{7542c78 u0 com.android.carsettings}
01-01 16:00:07.573 1704 1729 W InputWindow: getSurfaceTouchableRegion-3, region = SkRegion((-2560,-1440,5120,2880)), attrs= {(163,127)(720x720) gr=TOP END CENTER sim={adjust=resize} ty=NAVIGATION_BAR_PANEL fmt=RGBA_8888
01-01 16:00:07.573 1704 1729 W InputWindow: fl=LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS WATCH_OUTSIDE_TOUCH HARDWARE_ACCELERATED
01-01 16:00:07.573 1704 1729 W InputWindow: pfl=USE_BLAST
01-01 16:00:07.573 1704 1729 W InputWindow: bhv=DEFAULT
01-01 16:00:07.573 1704 1729 W InputWindow: fitTypes=NAVIGATION_BARS CAPTION_BAR}, this = Window{7542c78 u0 com.android.carsettings}
01-01 16:00:07.574 1704 1729 W InputWindow: getSurfaceTouchableRegion-5, region = SkRegion((-4237,-1567,3443,2753)), attrs= {(163,127)(720x720) gr=TOP END CENTER sim={adjust=resize} ty=NAVIGATION_BAR_PANEL fmt=RGBA_8888
01-01 16:00:07.574 1704 1729 W InputWindow: fl=LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS WATCH_OUTSIDE_TOUCH HARDWARE_ACCELERATED
01-01 16:00:07.574 1704 1729 W InputWindow: pfl=USE_BLAST
01-01 16:00:07.574 1704 1729 W InputWindow: bhv=DEFAULT
01-01 16:00:07.574 1704 1729 W InputWindow: fitTypes=NAVIGATION_BARS CAPTION_BAR}, this = Window{7542c78 u0 com.android.carsettings}
01-01 16:00:07.575 1704 1729 W InputWindow: getSurfaceTouchableRegion, region = SkRegion((-4237,-1567,3443,2753)), attrs= {(163,127)(720x720) gr=TOP END CENTER sim={adjust=resize} ty=NAVIGATION_BAR_PANEL fmt=RGBA_8888
01-01 16:00:07.575 1704 1729 W InputWindow: fl=LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS WATCH_OUTSIDE_TOUCH HARDWARE_ACCELERATED
01-01 16:00:07.575 1704 1729 W InputWindow: pfl=USE_BLAST
01-01 16:00:07.575 1704 1729 W InputWindow: bhv=DEFAULT
01-01 16:00:07.575 1704 1729 W InputWindow: fitTypes=NAVIGATION_BARS CAPTION_BAR}, this = Window{7542c78 u0 com.android.carsettings}, mInvGlobalScale = 1.0
01-01 16:00:07.575 1704 1729 W InputWindow: setTouchableRegion, region = SkRegion((-4237,-1567,3443,2753)), mHandle = 7542c78 com.android.carsettings, frame=[0,0,0,0], touchableRegion=SkRegion((-4237,-1567,3443,2753)), scaleFactor=1.0, transform=null, windowToken=android.os.BinderProxy@30d22ea, isClone=false, callstack =
01-01 16:00:07.575 1704 1729 W InputWindow: java.lang.Throwable: xxxx
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputWindowHandleWrapper.setTouchableRegion(InputWindowHandleWrapper.java:143)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor.populateInputWindowHandle(InputMonitor.java:316)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor$UpdateInputForAllWindowsConsumer.accept(InputMonitor.java:657)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor$UpdateInputForAllWindowsConsumer.accept(InputMonitor.java:529)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2728)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply(WindowContainer.java:2718)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowState.applyInOrderWithImeWindows(WindowState.java:4957)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowState.forAllWindows(WindowState.java:4801)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1699)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1699)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1699)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1699)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.WindowContainer.forAllWindows(WindowContainer.java:1716)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor$UpdateInputForAllWindowsConsumer.updateInputWindows(InputMonitor.java:569)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor$UpdateInputForAllWindowsConsumer.-$$Nest$mupdateInputWindows(Unknown Source:0)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.wm.InputMonitor$UpdateInputWindows.run(InputMonitor.java:137)
01-01 16:00:07.575 1704 1729 W InputWindow: at android.os.Handler.handleCallback(Handler.java:942)
01-01 16:00:07.575 1704 1729 W InputWindow: at android.os.Handler.dispatchMessage(Handler.java:99)
01-01 16:00:07.575 1704 1729 W InputWindow: at android.os.Looper.loopOnce(Looper.java:201)
01-01 16:00:07.575 1704 1729 W InputWindow: at android.os.Looper.loop(Looper.java:288)
01-01 16:00:07.575 1704 1729 W InputWindow: at android.os.HandlerThread.run(HandlerThread.java:67)
01-01 16:00:07.575 1704 1729 W InputWindow: at com.android.server.ServiceThread.run(ServiceThread.java:44)
wifi列表弹框,没有FLAG_NOT_TOUCH_MODAL ,且没有FLAG_NOT_FOCUSABLE,这样的行为,就是让所有的input event全部给这个window,所以出现bug这个的,点击window之外的区域,不会退出的现象。
3.问题解决方案
wifi列表弹框,加上FLAG_NOT_FOCUSABLE,最终验证PASS。