Tasker 与 WireGuard:为一个隧道发送正确的 Intent
Tasker 可以通过远程控制接收器请 WireGuard 拉起一个指定名称的隧道。控制方仍然需要 CONTROL_TUNNELS 权限。
启动 WireGuard 的 Activity 只会把应用带到前台,并不会拉起某个隧道。那种打开应用然后等待的自动化在测试中能通过,但第一次遇到手机已经停在主屏时就会失败。WireGuard 为具名隧道提供了一条受支持的远程控制路径,它比大多数教程所说的更窄。下面的清单沿着这条路径,从权限一直走到可观察的结果。
确认远程控制已启用
远程控制接收器并不总是处于监听状态。WireGuard 的设置里有一个启用隧道远程控制的选项,在它打开之前,接收器会直接忽略请求,而不是大声报错。默认关闭是有意为之,因为一个能拉起隧道的对外接收器,会变成每一台安装了该应用的设备上的安全漏洞。请在界面中启用它,并记下你这么做的时间,因为后来的使用者可能在不知道有谁依赖它的情况下把它关掉。
上面的接收器只处理具名的 UP 和 DOWN 请求,因此这里没有创建隧道或修改其配置的路径。在依赖它之前,先确认已安装的版本里确实有这个组件:adb shell dumpsys package com.wireguard.android | grep -i receiver 会列出已声明的接收器,而缺少远程控制接收器的版本永远不会响应。如果工作流需要另一个端点或另一个对端,那就是另一个隧道,需要在应用内单独手动配置。
确认控制方持有什么权限
WireGuard 用一个危险权限保护该接收器,即 com.wireguard.android.permission.CONTROL_TUNNELS,控制方应用必须持有它。自定义广播无法凭空造出发送方从未声明、也从未被授予的权限。自动化教程容易悄悄假定自动化拥有它其实没有的能力,问题就出在这一步。
写任务之前,两边都要查。第一条命令列出已声明的权限,第二条命令显示控制方包是否真的持有它。在清单里声明了权限、但运行时从未被授予的控制方仍然会失败,而且失败表现为沉默而不是弹窗。
adb shell pm list permissions -g -d | grep -i control_tunnels
adb shell dumpsys package net.dinglisch.android.taskerm | grep -i control_tunnels
如果控制方没有该权限,就使用应用真正支持的集成方式,而不是手工拼一个广播。发送 Intent 并看到调用成功,只能证明 Android 接受了它,不能证明 WireGuard 据此动作。把未经验证的发送当作未知,而不是当作成功。
对照已安装的应用核对 Intent 字段
四个字段承载整个请求,每一个的失败方式看起来都和其他几个一模一样。把动作、隧道 extra、包名和目标作为一组一起设对。
Action: com.wireguard.android.action.SET_TUNNEL_UP
Extra: tunnel:lab-wg
Package: com.wireguard.android
Target: Broadcast Receiver
动作只写 UP 或 DOWN,extra 携带完全一致的已配置隧道名,包名把投递限制在 WireGuard 上,目标必须是接收器而不是 Activity。拼错的隧道名产生的沉默和接收器被关闭时完全一样,所以在断定自动化坏了之前,先到应用里读一遍名字。DOWN 动作是 com.wireguard.android.action.SET_TUNNEL_DOWN,extra 和包名都与上面相同。测试时一次只发一个请求,因为两个互相抢跑的广播会产生谁都说不清的状态。
在设备的 shell 里测试同一个请求,把 Intent 和自动化分开。如果那条命令失败而 Tasker 任务成功,差别在于发送方的权限,而不在字段值上。
adb shell am broadcast -a com.wireguard.android.action.SET_TUNNEL_UP -e tunnel lab-wg --receiver-permission com.wireguard.android.permission.CONTROL_TUNNELS
确认触发器覆盖不到的范围
应用启动或进入前台的事件可以发起连接请求,但在该事件被处理之前,应用可能已经做过一次名称解析或首次请求。触发器是在系统察觉到状态变化时触发的,那已经是应用开始工作之后了。如果应用绝不能在隧道之外通信,启动触发器就不是阻塞规则,加一个延时也不能把它变成阻塞规则。请为阻塞行为配置受支持的始终开启或按应用策略,触发器只保留作便利之用。
阻塞行为完全住在别的地方。adb shell settings get global always_on_vpn_app 给出被固定为始终开启的包名,adb shell dumpsys connectivity | grep -i vpn 显示系统当前跟踪的 VPN 网络。这两者都不是启动触发器能替代的。
同样的道理也适用于会话的另一端。因为一个窗口失去焦点就断开隧道,会破坏后台任务、认证辅助程序以及任何依赖该连接的应用。请定义有意的会话结束,而不是跟着焦点变化走;当自动化切换上下文时,也要避免快速连续的 UP 和 DOWN。
检查结果而不是发送
没有报错地返回,只说明 Android 接受了这个广播,仅此而已。隧道的状态要从应用里读,工作流是否就绪则要看下一步真正需要什么。这是两项不同的检查,通过第一项并不意味着第二项也通过。
也要直接读接口状态:adb shell ip -brief addr show tun0 会打印隧道接口及其地址,而存在却没有地址的接口,是起来了但不可用。随后还要验证私有服务本身,因为一个起来了却没有握手或没有路由的隧道,仍然会在真正关键的任务上失败。在真实权限边界内控制一个具名连接的自动化,不需要去点界面、执行 root 命令,也不需要削弱 Android 的网络防护。