Skip to content

Latest commit

 

History

History
273 lines (240 loc) · 13.8 KB

im-not-obscured.md

File metadata and controls

273 lines (240 loc) · 13.8 KB

View.FLAG_WINDOW_IS_OBSCURED

不知道什么时候开始 play 会检查窗口遮挡了,由于我日常开着 FV 悬浮球之类的工具,在 play 里面总是被弹提示,而且不能执行安装等操作,非常烦人。

反编译 + 搜索了一下,发现这个窗口遮挡的信息是从 MotionEvent 里面来的。

Android detect or block floating/overlaying apps - Stack Overflow

如果 View 设置了 setFilterTouchesWhenObscured ,并且触摸事件有 FLAG_WINDOW_IS_OBSCURED 这个 flag , View#onFilterTouchEventForSecurity 会返回 false 。自定义 View 可以重载这个方法,调用 super 看是不是 false 来判断是否被遮挡。

    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //noinspection RedundantIfStatement
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            // Window is obscured, drop this touch.
            return false;
        }
        return true;
    }

在 XML 中就是这样:

android:filterTouchesWhenObscured="true"

如果把这个过滤属性放在父 view ,可以阻止被遮挡的窗口下的点击事件向下传播

对应用来说,隐藏的最简单的方法就是 hook View#onFilterTouchEventForSecurity ,让它返回 true 。

如果从系统层面下手比较困难,因为 input 模块主要在 native 代码里面。

处理输入的代码被编译在动态库 libinput.so 中,input 事件通过 socket 传输(似乎是 binder + socket pair fd)。

// frameworks/native/include/input/InputTransport.h
status_t InputPublisher::publishMotionEvent ->
status_t sendMessage(const InputMessage* msg);

这个函数每个版本的签名几乎都不一样

    // 13
    status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
                                int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
                                int32_t actionButton, int32_t flags, int32_t edgeFlags,
                                int32_t metaState, int32_t buttonState,
                                MotionClassification classification, const ui::Transform& transform,
                                float xPrecision, float yPrecision, float xCursorPosition,
                                float yCursorPosition, const ui::Transform& rawTransform,
                                nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
                                const PointerProperties* pointerProperties,
                                const PointerCoords* pointerCoords);

    // 12.1
    status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
                                int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
                                int32_t actionButton, int32_t flags, int32_t edgeFlags,
                                int32_t metaState, int32_t buttonState,
                                MotionClassification classification, const ui::Transform& transform,
                                float xPrecision, float yPrecision, float xCursorPosition,
                                float yCursorPosition, uint32_t displayOrientation,
                                int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
                                nsecs_t eventTime, uint32_t pointerCount,
                                const PointerProperties* pointerProperties,
                                const PointerCoords* pointerCoords);

    // 12
    status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
                                int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
                                int32_t actionButton, int32_t flags, int32_t edgeFlags,
                                int32_t metaState, int32_t buttonState,
                                MotionClassification classification, const ui::Transform& transform,
                                float xPrecision, float yPrecision, float xCursorPosition,
                                float yCursorPosition, int32_t displayWidth, int32_t displayHeight,
                                nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
                                const PointerProperties* pointerProperties,
                                const PointerCoords* pointerCoords);

    // 11
    status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
                                int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
                                int32_t actionButton, int32_t flags, int32_t edgeFlags,
                                int32_t metaState, int32_t buttonState,
                                MotionClassification classification, float xScale, float yScale,
                                float xOffset, float yOffset, float xPrecision, float yPrecision,
                                float xCursorPosition, float yCursorPosition, nsecs_t downTime,
                                nsecs_t eventTime, uint32_t pointerCount,
                                const PointerProperties* pointerProperties,
                                const PointerCoords* pointerCoords);

    // 10.0
    status_t publishMotionEvent(
            uint32_t seq,
            int32_t deviceId,
            int32_t source,
            int32_t displayId,
            int32_t action,
            int32_t actionButton,
            int32_t flags,
            int32_t edgeFlags,
            int32_t metaState,
            int32_t buttonState,
            MotionClassification classification,
            float xOffset,
            float yOffset,
            float xPrecision,
            float yPrecision,
            nsecs_t downTime,
            nsecs_t eventTime,
            uint32_t pointerCount,
            const PointerProperties* pointerProperties,
            const PointerCoords* pointerCoords);

    // 9.0, 8.1
    status_t publishMotionEvent(
            uint32_t seq,
            int32_t deviceId,
            int32_t source,
            int32_t displayId,
            int32_t action,
            int32_t actionButton,
            int32_t flags,
            int32_t edgeFlags,
            int32_t metaState,
            int32_t buttonState,
            float xOffset,
            float yOffset,
            float xPrecision,
            float yPrecision,
            nsecs_t downTime,
            nsecs_t eventTime,
            uint32_t pointerCount,
            const PointerProperties* pointerProperties,
            const PointerCoords* pointerCoords);

    // 8.0
    status_t publishMotionEvent(
            uint32_t seq,
            int32_t deviceId,
            int32_t source,
            int32_t action,
            int32_t actionButton,
            int32_t flags,
            int32_t edgeFlags,
            int32_t metaState,
            int32_t buttonState,
            float xOffset,
            float yOffset,
            float xPrecision,
            float yPrecision,
            nsecs_t downTime,
            nsecs_t eventTime,
            uint32_t pointerCount,
            const PointerProperties* pointerProperties,
            const PointerCoords* pointerCoords);

上面的 publishMotionEvent 符号在 libinput.so 被导出,且被 libinputflinger.so 引用,可以考虑用 plt hook 。

_ZN7android14InputPublisher18publishMotionEventEjiiiiNSt3__15arrayIhLm32EEEiiiiiiNS_20MotionClassificationEfffffffflljPKNS_17PointerPropertiesEPKNS_13PointerCoordsE

我们看看这个「遮挡」的标准是什么:

master

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
static bool canBeObscuredBy(const sp<WindowInfoHandle>& windowHandle,
                            const sp<WindowInfoHandle>& otherHandle) {
    // Compare by token so cloned layers aren't counted
    if (haveSameToken(windowHandle, otherHandle)) {
        return false;
    }
    auto info = windowHandle->getInfo();
    auto otherInfo = otherHandle->getInfo();
    if (otherInfo->inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
        return false;
    } else if (otherInfo->alpha == 0 &&
               otherInfo->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE)) {
        // Those act as if they were invisible, so we don't need to flag them.
        // We do want to potentially flag touchable windows even if they have 0
        // opacity, since they can consume touches and alter the effects of the
        // user interaction (eg. apps that rely on
        // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
        // windows), hence we also check for FLAG_NOT_TOUCHABLE.
        return false;
    } else if (info->ownerUid == otherInfo->ownerUid) {
        // If ownerUid is the same we don't generate occlusion events as there
        // is no security boundary within an uid.
        return false;
    } else if (otherInfo->inputConfig.test(gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)) {
        return false;
    } else if (otherInfo->displayId != info->displayId) {
        return false;
    }
    return true;
}

android 11

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle,
                            const sp<InputWindowHandle>& otherHandle) {
    // Compare by token so cloned layers aren't counted
    if (haveSameToken(windowHandle, otherHandle)) {
        return false;
    }
    auto info = windowHandle->getInfo();
    auto otherInfo = otherHandle->getInfo();
    if (!otherInfo->visible) {
        return false;
    } else if (info->ownerPid == otherInfo->ownerPid) {
        // If ownerPid is the same we don't generate occlusion events as there
        // is no in-process security boundary.
        return false;
    } else if (otherInfo->isTrustedOverlay()) {
        return false;
    } else if (otherInfo->displayId != info->displayId) {
        return false;
    }
    return true;
}

trusted overlay

// frameworks/native/libs/input/InputWindow.cpp
bool InputWindowInfo::isTrustedOverlay() const {
    return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
            layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
            layoutParamsType == TYPE_NOTIFICATION_SHADE ||
            layoutParamsType == TYPE_NAVIGATION_BAR ||
            layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
            layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
            layoutParamsType == TYPE_DOCK_DIVIDER ||
            layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
            layoutParamsType == TYPE_INPUT_CONSUMER ||
            layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
}

Android 11 中,TYPE_ACCESSIBILITY_OVERLAY 是可信的,因此不会被看作是遮挡。

不过我日常用的 FV 悬浮球有一些 application overlay 的窗口(type 2038, 0x7f6),其中最后一个窗口覆盖了整个屏幕,而且是 NOT_TOUCHABLE 的,因此被检测到了。

# dumpsys input
      4: name='Window{e79218b mode=1 rootTaskId=1 u0 com.fooview.android.fooview}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00010308, type=0x000007f6, frame=[0,908][33,1128], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[0,908][33,1128], inputFeatures=0x00000000, ownerPid=5138, ownerUid=10211, dispatchingTimeout=8000ms
      5: name='Window{240c017 mode=1 rootTaskId=1 u0 com.fooview.android.fooview}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00020018, type=0x000007f6, frame=[971,95][972,2356], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[971,95][972,2356], inputFeatures=0x00000000, ownerPid=5138, ownerUid=10211, dispatchingTimeout=8000ms
      12: name='Window{a5aef30 mode=1 rootTaskId=1 u0 com.fooview.android.fooview}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00010318, type=0x000007f6, frame=[0,2355][1080,2356], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[0,2355][1080,2356], inputFeatures=0x00000000, ownerPid=5138, ownerUid=10211, dispatchingTimeout=8000ms

# dumpsys window
  Window #22 Window{a5aef30 mode=1 rootTaskId=1 u0 com.fooview.android.fooview}:
    mDisplayId=0 rootTaskId=1 mSession=Session{d602148 5138:u0a10211} mClient=android.os.BinderProxy@8bd48e2
    mOwnerUid=10211 showForAllUsers=false package=com.fooview.android.fooview appop=SYSTEM_ALERT_WINDOW
    mAttrs={(0,0)(fillx1) gr=BOTTOM LEFT CENTER sim={adjust=pan} blurRatio=1.0 blurMode=0 ty=APPLICATION_OVERLAY fmt=TRANSPARENT
      fl=NOT_FOCUSABLE NOT_TOUCHABLE LAYOUT_IN_SCREEN LAYOUT_NO_LIMITS LAYOUT_INSET_DECOR
      pfl=
      fitTypes=NAVIGATION_BARS CAPTION_BAR}
    Requested w=1080 h=1 mLayoutSeq=32042
    mHasSurface=true isReadyForDisplay()=true mWindowRemovalAllowed=false
    WindowStateAnimator{290a64 }:
      Surface: shown=true layer=0 alpha=1.0 rect=(0.0,0.0) 1080 x 1 transform=(1.0, 0.0, 1.0, 0.0)