背景
来自于一个网友在技术交流群中问题,我正好之前开发过程中也遇到了类似的问题,写个文章记录一下。
代码语言:javascript复制InputDispatcher: Untrusted touch due to occlusion by com.xx.xx/10074 (obscuring opacity = 1.00, maximum allowed = 0.80)
InputDispatcher: Dropping untrusted touch event due to com.xx.xx/10074
大佬们,请教个问题哈,测试时发现launcher上有个app,点击按钮没反应,然后捞了下日志,发现有输出上述这2行日志。
请问下大佬们,这2行日志是干啥的呢?
一、这个功能到底有什么用
保护你的手机操作的安全,避免你误点了某个功能,怎么理解这件事?
举个例子,假如悬浮窗口B设置成了可穿透的触摸模式,就是touch事件可以穿透到应用A,那用户在不清楚状况的情况下,以为点击了紫色的“取消"按钮,最后生效的是绿色的“付款“”按钮那不是很危险吗?
为了维持系统安全并保持良好的用户体验,Android 12 会阻止应用使用[触摸事件],也就是说系统会屏蔽穿透某些窗口的触摸操作。
图中就应该屏蔽点击取消的触摸事件,阻止应用A使用这次触摸事件
二、受影响的应用
此变更会影响选择让触摸操作穿透其窗口的应用,例如使用 [FLAG_NOT_TOUCHABLE
]标志,但不限于以下示例:
- 需要
SYSTEM_ALERT_WINDOW
权限并使用FLAG_NOT_TOUCHABLE
标志的叠加层,例如使用TYPE_APPLICATION_OVERLAY
的窗口。 - 使用
FLAG_NOT_TOUCHABLE
标志的 activity 窗口。
三、允许被透传的例外情况
3.1 应用中的互动。您的应用会显示叠加层,并且只有当用户与您的应用进行互动时才会显示叠加层。
这个很好理解,以为都是你app内部的事,不用担心有什么不安全的,除非app自己想搞事情
3.2 可信窗口。包括但不限于以下窗口:
无障碍窗口 输入法 (IME) 窗口 Google 助理窗口
注意:类型为 TYPE_APPLICATION_OVERLAY 的窗口不受信任。
这个也好理解,毕竟都是系统内部的窗口
3.3 不可见窗口。窗口的根视图是 GONE 或 INVISIBLE。
不会造成用户的误解
3.4 全透明窗口。窗口的 alpha 属性为 0.0。
与3.3理由一样
3.5 足够半透明的系统警报窗口。当组合后的不透明度小于或等于系统针对触摸的最大遮掩不透明度时,系统会将一组系统警报窗口视为足够半透明。在 Android 12 中,默认最大不透明度为 0.8。
这个网友遇到的错误就是obscuring opacity = 1.00, maximum allowed = 0.80
,不透明度1.00,怎么能允许被穿透呢,所以这个应用写的就是不符合规范。
只有让用户可以有足够的透明度知道自己点击的是后面那个窗口,那才是受信任的触摸。
四、检测不受信任的触摸操作是否被屏蔽
可以通过adb日志查看
代码语言:javascript复制Untrusted touch due to occlusion by PACKAGE_NAME
如需允许不受信任的触摸操作,请在终端窗口中运行以下 ADB 命令
代码语言:javascript复制# A specific app
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app
# All apps
# If you'd still like to see a Logcat message warning when a touch would be
# blocked, use 1 instead of 0.
adb shell settings put global block_untrusted_touches 0
如需将行为还原为默认设置(不受信任的触摸操作被屏蔽),请运行以下命令:
代码语言:javascript复制# A specific app
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app
# All apps
adb shell settings put global block_untrusted_touches 2
这里注意block_untrusted_touches为2是打开功能,1是关闭这个功能,但是还有日志输出,0是彻底关闭这个功能,没有日志输出。
五、代码
具体的代码实现在findTouchedWindowTargetsLocked的逻辑中,会调用computeTouchOcclusionInfoLocked算出TouchOcclusionInfo,然后再调用isTouchTrustedLocked确认是否是信任的触摸事件,有兴趣的可以看看实现细节。
代码语言:javascript复制InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
...
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
// Drop events that can't be trusted due to occlusion
if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
TouchOcclusionInfo occlusionInfo =
computeTouchOcclusionInfoLocked(windowHandle, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
for (const auto& log : occlusionInfo.debugInfo) {
ALOGD("%s", log.c_str());
}
}
sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
ALOGW("Dropping untrusted touch event due to %s/%d",
occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
continue;
}
}
}
}
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
if (occlusionInfo.hasBlockingOcclusion) {
ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
occlusionInfo.obscuringUid);
return false;
}
if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
"%.2f, maximum allowed = %.2f)",
occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
return false;
}
return true;
}
参考文献
https://developer.android.google.cn/about/versions/12/behavior-changes-all#untrusted-touch-events