Android12特性“不受信任的触摸事件被屏蔽”其他细节
android12-release
Android 12关于Input触摸事件的行为变更 这篇介绍了input实际的拦截findTouchedWindowTargetsLocked
“不受信任的触摸事件被屏蔽”相关属性
mBlockUntrustedTouchesMode
- 三种属性值:
DISABLED不拦截;PERMISSIVE拦截判断最后没拦截;BLOCK拦截
frameworks\base\core\java\android\hardware\input\InputManager.java
frameworks/native/libs/input/android/os/BlockUntrustedTouchesMode.aidl
/** @hide */
public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = {
BlockUntrustedTouchesMode.DISABLED,
BlockUntrustedTouchesMode.PERMISSIVE,
BlockUntrustedTouchesMode.BLOCK
};
- block_untrusted_touches设置/获取:
Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE改变 -- InputManagerService.java -- JNI -- InputDispatcher.cpp
adb shell settings get global block_untrusted_touches
adb shell settings put global block_untrusted_touches 0
- 单个应用设置
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app
frameworks/base/cmds/am/src/com/android/commands/am/Am.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
frameworks/base/services/core/java/com/android/server/compat/PlatformCompat.java
frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
frameworks/base/core/java/android/app/compat/CompatChanges.java
frameworks/base/core/java/android/app/compat/ChangeIdStateCache.java
frameworks/base/core/java/android/app/compat/ChangeIdStateQuery.java
TouchOcclusionInfo
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.h
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
float obscuringOpacity;
std::string obscuringPackage;
int32_t obscuringUid;
std::vector<std::string> debugInfo;
};
computeTouchOcclusionInfoLocked
赋值
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
- hasBlockingOcclusion 界面InputWindowInfo属性
touchOcclusionMode==BLOCK_UNTRUSTED
时为true- obscuringOpacity 界面InputWindowInfo属性
touchOcclusionMode==USE_OPACITY
时计算透明度- obscuringUid、obscuringPackage 界面InputWindowInfo属性
touchOcclusionMode!=ALLOW
时会赋值- debugInfo 当DEBUG_TOUCH_OCCLUSION打开时赋值界面信息InputWindowInfo
canBeObscuredBy(windowHandle, otherHandle)
当InputWindowHandle可见visible、不信任trustedOverlay等时赋值,否则,返回TouchOcclusionInfo默认值
InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
int32_t displayId = windowInfo->displayId;
const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
info.obscuringOpacity = 0;
info.obscuringUid = -1;
std::map<int32_t, float> opacityByUid;
for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
break; // All future windows are below us. Exit early.
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) &&
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
}
// canBeObscuredBy() has returned true above, which means this window is untrusted, so
// we perform the checks below to see if the touch can be propagated or not based on the
// window's touch occlusion mode
if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
info.hasBlockingOcclusion = true;
info.obscuringUid = otherInfo->ownerUid;
info.obscuringPackage = otherInfo->packageName;
break;
}
if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
uint32_t uid = otherInfo->ownerUid;
float opacity =
(opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
// Given windows A and B:
// opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
opacityByUid[uid] = opacity;
if (opacity > info.obscuringOpacity) {
info.obscuringOpacity = opacity;
info.obscuringUid = uid;
info.obscuringPackage = otherInfo->packageName;
}
}
}
}
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
}
return info;
}
!isTouchTrustedLocked
根据TouchOcclusionInfo判断是否拦截
- hasBlockingOcclusion 界面InputWindowInfo属性
touchOcclusionMode==BLOCK_UNTRUSTED
时拦截- 模糊不透明度obscuringOpacity大于mMaximumObscuringOpacityForTouch时拦截(mMaximumObscuringOpacityForTouch为
Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH)
属性值,默认0.8f)
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;
}
什么情况不拦截
回顾上边属性,并查看Android 12关于Input触摸事件的行为变更图代码:
// Drop events that can't be trusted due to occlusion
if (newTouchedWindowHandle != nullptr &&
mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
TouchOcclusionInfo occlusionInfo =
computeTouchOcclusionInfoLocked(newTouchedWindowHandle, 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());
}
}
onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
ALOGW("Dropping untrusted touch event due to %s/%d",
occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
newTouchedWindowHandle = nullptr;
}
}
}
- BlockUntrustedTouchesMode为
DISABLED、PERMISSIVE
不拦截- 当窗口InputWindowHandle可见visible、信任trustedOverlay时TouchOcclusionInfo默认
!isTouchTrustedLocked
为false,不拦截- 当窗口InputWindowInfo属性
touchOcclusionMode=ALLOW
时TouchOcclusionInfo默认!isTouchTrustedLocked
为false,不拦截- 设置
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app
时com.example.app该应用不拦截(实质同第3种touchOcclusionMode=ALLOW一样
)
拦截通知弹框
onUntrustedTouchLocked(occlusionInfo.obscuringPackage) -- JNI -- InputManagerService.java
- UNTRUSTED_TOUCHES_TOAST默认关闭Toast弹框
- PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST 配置不弹框
private static final String[] PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST = { "com.snapchat.android" // b/173297887 };