你点击 VPN 授权按钮,对话框却毫无反应。人们很自然地认为 VPN 应用或服务器坏了,于是重置密码、更换 DNS 或重装客户端。但通常这些操作都碰不到故障点,因为安卓会丢弃穿过遮挡层到达的触摸事件,而 VPN 授权界面恰恰属于会受到这种保护的敏感同意提示。点击根本没有到达按钮。

防护到底在拒绝什么

安卓通过 tapjacking 防护来保护敏感交互:另一个窗口压在对话框之上,要么窃取点击,要么让点击变得含糊。视图通过 View.setFilterTouchesWhenObscured 启用该防护,当另一个窗口被标记为部分或完全遮挡时,事件会携带 MotionEvent.FLAG_WINDOW_IS_OBSCURED 到达。此时事件会被丢弃,而不是送到错误的位置。Android 12(API 31)在系统同意界面上进一步加入了不可信触摸拦截,因此授权对话框可能拒绝来自悬浮层的点击,而其他应用仍会接受这种点击。

有三类东西经常造成遮挡:持有“显示在其他应用上层”权限的应用、绘制内容的无障碍服务,以及录屏或滤镜工具提供的浮动小部件。某些厂商的显示增强功能也会叠加自己的图层,这就是同一个对话框在两台同版本安卓手机上表现不同的原因。部分遮挡的窗口会设置 FLAG_WINDOW_IS_PARTIALLY_OBSCURED,触摸在按钮看到之前就被丢弃了。这是屏幕层面的属性,与网络无关。点击被丢弃的那一刻,没有任何数据发往 VPN 服务器,因此服务器端的任何改动都无法影响结果。

把“点击无效”与“连接失败”分开

这个区分就是全部诊断,只需十秒。如果点击被忽略、对话框原样停留,遮挡是本地的。如果对话框关闭、随后出现错误,说明同意已被接受,你已经进入另一个阶段,需要 VPN 应用自身的事件信息。

你可以通过检查发起请求的应用是否真的尝试过连接来确认本地判断。同意对话框显示在屏幕上期间从不记录握手尝试的客户端,根本没有到达网络。

adb shell cmd appops get com.example.vpn SYSTEM_ALERT_WINDOW
adb shell settings get secure enabled_accessibility_services

列出谁持有悬浮层权限、哪些无障碍服务处于启用状态,再与正在运行的程序对照。不要关闭平台自身的防护,正是它阻止隐藏窗口替你回答同意提示。

清除遮挡,然后重走正常流程

先确认是哪个应用请求了 VPN 授权,这样你才知道自己在授予哪一种同意。然后通过它自身的设置停掉那个具体的悬浮层,而不是撤销一个以后可能还要用的宽泛权限。屏幕滤镜通常在自己的应用里有开关;浮动小部件有关闭控件;厂商显示模式在显示设置里有开关。

之后回到 VPN 应用重新请求授权,让对话框重新创建。如果点击现在有效,可以调整悬浮层的兼容行为,或者在连接期间保持关闭,同时让触摸防护保持开启。

如果分不清是哪个窗口造成的,就一次移除一个,每次改动后重试同意对话框。录屏控制条或浮动快捷方式通常是元凶,而在成功之前刚移除的那个就是答案。

把沿途关掉的不相关项目恢复回去。屏幕滤镜或夜间模式不是敌人,与对话框重叠的那个具体窗口才是。

为了绕过对话框,哪些事不能做

不要给不熟悉的应用授予无障碍权限让它替你按按钮,也不要为此安装辅助应用。它们给出的正是这套防护要拒绝的能力,而且是在一个即将授权 VPN 的界面上。

也不要用输入工具自动化这个对话框。它带来的结果与恶意悬浮层相同,而且不会让你了解任何成因。

在受管设备上,限制 VPN 变更的策略是管理员的决定。做悬浮层实验并不等于获得绕过它的授权。同样,同意按钮没反应也不是削弱证书校验或更改账号凭据的理由:这些设置都清不掉本地的触控遮挡。

如果仍然失败,请报告确切表现

描述对话框,而不是给出结论:哪个应用请求了授权、对话框是保持打开还是关闭、其他敏感提示(例如未知来源或无障碍警告)是否同样忽略点击、设备型号与安卓版本,以及你抓到的悬浮层与无障碍列表。

顺便再问支持方一个问题:设备策略是否限制了 VPN 配置变更。受管封锁的表现与被忽略的点击不同,知道自己面对的是哪一种能省掉一次往返。

这一组信息能把“触摸被遮挡”与输入设备损坏、对话框卡死或策略封锁区分开来,也能告诉下一位处理者该看屏幕还是该看网络。

安卓 tapjacking 指南涵盖了遮挡窗口的条件以及这种行为背后的 API。如果想留下证据链,就在那个始终没有到达按钮的事件里查找 FLAG_WINDOW_IS_OBSCURED 和 FLAG_WINDOW_IS_PARTIALLY_OBSCURED。要抓取的对象是那个保持打开的对话框,因为它说明点击是被拦下了,而不是被送到了别的窗口。

相关文章