
最近,各类基于AI Agent的手机助手在社交平台上火爆出圈。它们展现出的能力令人惊叹:用户只需语音或文字下达指令,如“帮我找最便宜的iPhone”,AI就能自动打开购物App、精准搜索商品、比价并完成下单。这种“AI接管手机”的场景,无疑勾勒出了未来人机交互的新蓝图。
然而,当海量的AI操作开始涌现时,传统的移动端用户行为分析将面临严峻挑战。这些自动化的“非人”操作会严重污染业务数据,可能导致以下问题:
- 转换率虚高:AI的自动下单行为会推高转换率数据,干扰业务方对真实用户购买力的判断。
- 用户路径分析失效:AI执行的任务路径通常高度优化且重复,会污染真实的用户行为路径分析模型。
- 推荐算法偏差:基于被AI操作数据“污染”过的数据集进行训练的推荐模型,其推荐结果将逐渐偏离真实用户的兴趣偏好。
那么,我们该如何有效识别这些“非人”操作呢?首先,我们需要从技术层面拆解AI或脚本是如何操作手机的。
AI Agent操作手机的技术原理
上图清晰地展示了AI Agent操作手机的典型流程,主要可分为四个层次:
- 用户入口层:用户通过文字或语音下达操作指令。
- 屏幕捕获层:Agent获取当前手机的屏幕信息(图像或视图结构)。
- 云端通信层:屏幕信息被上传至云端推理服务器,服务器分析后生成下一步操作指令并返回。
- 操作执行层:在手机端执行具体的操作指令,如点击、滑动、输入等。
从移动端监控的角度出发,要识别“非人”操作,我们需要重点关注操作执行层。以Android平台为例,在操作执行层实现自动化操作主要有三种技术路径:
- 通过
AccessibilityService(无障碍服务)输入事件
- 通过
INJECT_EVENTS 权限注入事件
- 通过
adb shell input 命令注入事件
此外,通过定制ROM、外接硬件等方式也能实现自动化,但不在本文讨论范围内。
通过 AccessibilityService 输入事件
AccessibilityService 是Android系统为辅助残障人士使用设备而提供的框架,但它也被广泛用于实现自动化操作,是各类辅助功能App和游戏脚本工具的主要技术手段。
其工作机制可分为三个阶段:
-
阶段一:事件监听
当应用界面状态发生变化(例如新页面打开、按钮状态改变)时,系统会通过 AccessibilityEvent 通知所有已注册并启用的无障碍服务。服务可以监听多种事件类型,包括窗口状态变化、内容变化、视图滚动等。
-
阶段二:屏幕读取
无障碍服务能够获取当前活动窗口的完整视图层次结构,通过 AccessibilityNodeInfo 对象访问屏幕上所有的UI元素,包括:
- 文本内容(按钮文字、输入框内容等)
- 视图属性(位置、大小、是否可点击等)
- 视图的层次关系(父子节点、兄弟节点等)
这使得AI Agent能够“看见”并理解当前屏幕的内容和状态。
-
阶段三:自动化操作
基于读取的屏幕信息,服务可以执行两种操作:
- 节点操作:直接对特定的UI节点执行点击、长按、输入文本等操作。
- 手势操作:通过
GestureDescription API执行复杂的滑动、拖拽等多点触控手势。
通过无障碍服务注入事件的特点:
- 需要用户显式授权:用户必须在系统设置中手动开启对应的无障碍服务。
- 能完整读取屏幕内容:可以获取文本、视图结构等丰富信息。
- 操作能力灵活全面:支持点击、滑动、输入等复杂交互。
通过 INJECT_EVENTS 注入事件
INJECT_EVENTS 是一个系统级权限,允许应用直接向Android输入子系统注入触摸或按键事件,从而模拟真实用户操作。这是一种更底层的注入机制。
其工作机制也分为三个阶段:
-
阶段一:事件构造
应用通过 Instrumentation 或反射调用系统API,构造包含坐标、动作类型等信息的 MotionEvent 对象。
-
阶段二:权限验证
Android系统会检查调用者是否持有 INJECT_EVENTS 权限。该权限是系统级,普通应用无法获取,通常只有以下情况可以使用:
-
阶段三:系统注入
通过权限验证后,事件被直接注入到Android的输入子系统。该系统负责处理所有硬件输入事件,因此注入的事件会被当作真实的触摸事件处理,并分发给当前获得焦点的窗口。
通过 INJECT_EVENTS 注入事件的特点:
- 底层注入:事件绕过应用层,直接在系统底层产生。
- 无需用户手动授权:但需要系统签名或Root权限。
- 更难被检测:由于发生在更底层,应用层常规检测手段可能失效。
adb shell input 是Android调试桥提供的命令行工具,用于通过USB或网络连接向设备注入输入事件,常见于开发调试和自动化测试。其本质与 INJECT_EVENTS 相同,主要区别在于调用主体和权限获取方式。
其工作机制主要分为四个阶段:
- 阶段一:命令发送
在PC或远程设备上,通过ADB客户端发送input命令,例如:
adb shell input tap 500 1000 # 点击坐标 (500, 1000)
adb shell input swipe 100 200 300 400 # 从 (100, 200) 滑动到 (300, 400)
adb shell input text "hello" # 输入文本 “hello”
- 阶段二:ADB协议传输
ADB客户端通过USB或TCP/IP将命令发送至设备端的ADB守护进程。
- 阶段三:守护进程处理
设备端的 adbd 进程解析命令,构造相应的 MotionEvent 或 KeyEvent 对象。adbd 通常以 shell 或 root 权限运行。
- 阶段四:系统注入
adbd 调用系统API将事件注入输入子系统,此后的路径与 INJECT_EVENTS 方式完全一致。
与 INJECT_EVENTS 对比,adb shell input 方式的特点:
- 需要建立ADB连接:事件通过ADB协议传输。
- 权限来自ADB连接:只要建立了ADB调试连接即可,无需修改应用本身。
- 底层实现一致:最终的事件注入路径相同。
如何检测“非人”操作
不少脚本工具,包括能操作手机的AI Agent,都在使用上述方案。但需注意,视障用户等群体也合法依赖无障碍服务。因此,简单粗暴的特征匹配容易造成误判。下文将探讨如何在操作发生时,采集事件特征及环境信息,来辅助识别“非人”操作。
识别 AccessibilityService 输入事件
通过无障碍服务操作手机,前提是用户在系统设置中开启它。Android系统提供了API来检测无障碍服务的状态:
// 1. 检测是否存在正在运行的无障碍服务
public boolean hasAccessibilityServiceRunning() {
AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
return am != null && am.isEnabled();
}
// 2. 检测无障碍服务的ID
public void checkServiceId() {
List<AccessibilityServiceInfo> enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo service : enabledServices) {
// 获取服务 ID (通常是 "包名/类名")
String id = service.getId();
}
}
// 3. 检测是否具备操作应用的能力(全控能力)
public boolean hasFullControlAgent() {
List<AccessibilityServiceInfo> enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
for (AccessibilityServiceInfo service : enabledServices) {
int capabilities = service.getCapabilities();
// 1. 检查是否可以读取屏幕内容
boolean canRetrieve = (capabilities & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
// 2. 检查是否可以执行手势操作
boolean canPerform = (capabilities & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0;
// 同时具备读取和操作能力,则认为是全控Agent
if (canRetrieve && canPerform) {
return true;
}
}
return false;
}
此外,在Android S(API 31)及以上版本,还可以检查 MotionEvent 的标志位来判断事件是否源自无障碍服务:
// 检测事件是否由无障碍服务产生(API 31+)
public boolean isAccessibilityEvent(MotionEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
int flags = event.getFlags();
// 使用位运算检查是否包含 FLAG_IS_ACCESSIBILITY_EVENT (0x800)
return (flags & 0x0800) != 0;
}
return false;
}
应用可以检测到无障碍服务的存在,但需注意避免对合法的辅助功能造成误伤。
识别 INJECT_EVENTS 注入事件
通过 INJECT_EVENTS 注入的事件通常具有以下特征:
- 事件属性可能缺失压力值、触摸面积等真实触摸信息。
- 事件标志位可能包含
FLAG_IS_GENERATED_GESTURE。
- 事件来源可能被标记为
SOURCE_UNKNOWN。
检测逻辑示例如下:
public boolean isEventInjected(MotionEvent event) {
if (event == null) {
return false;
}
// 方法1:检查事件属性(可能误判)
boolean hasPressure = event.getPressure() > 0;
boolean hasSize = event.getSize() > 0;
boolean hasToolType = event.getToolType(0) != MotionEvent.TOOL_TYPE_UNKNOWN;
if (!hasPressure && !hasSize && !hasToolType) {
return true; // 属性严重缺失,疑似注入
}
// 方法2:检查事件标志位
int flags = event.getFlags();
if ((flags & 0x08000000) != 0) { // FLAG_IS_GENERATED_GESTURE
return true;
}
// 方法3:检查事件来源
int source = event.getSource();
if (source == InputDevice.SOURCE_UNKNOWN) {
return true;
}
return false;
}
由于注入发生在底层,没有单一可靠的方法能100%准确识别。因此,对于这类事件,往往需要结合多维特征进行综合判断。
adb shell input 注入的事件本质与 INJECT_EVENTS 相同,但由于需要ADB连接,可以通过检测ADB环境状态来辅助识别。
检测ADB是否开启:
public static boolean isAdbEnabled(Context context) {
return Settings.Global.getInt(
context.getContentResolver(),
Settings.Global.ADB_ENABLED, 0
) > 0;
}
检测USB连接状态:
private static boolean isUsbConnected(Context context) {
Intent intent = context.registerReceiver(
null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (intent == null) return false;
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_USB;
}
检测ADB调试端口是否开放(适用于无线调试或模拟器场景):
private static boolean isAdbPortOpen() {
// 常见的 ADB 端口
int[] ports = {5555, 5554, 5556, 5557, 5558, 5559, 5560};
for (int port : ports) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress("127.0.0.1", port), 50);
Log.w(TAG, "isAdbPortOpen, opened. port: " + port);
return true;
} catch (Exception e) {
// ignored
e.printStackTrace();
}
}
return false;
}
检测调试器是否连接:
public static boolean isDebuggerAttached() {
return android.os.Debug.isDebuggerConnected();
}
检测USB ADB活动状态(需反射):
private static boolean isUsbAdbActive() {
try {
Class<?> systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method getMethod = systemPropertiesClass.getMethod("get", String.class);
String usbState = (String) getMethod.invoke(null, "sys.usb.state");
if (usbState != null && usbState.contains("adb")) {
return true;
}
// 双重校验
String usbConfig = (String) getMethod.invoke(null, "persist.sys.usb.config");
if (usbConfig != null && usbConfig.contains("adb")) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
通过以上方法,可以在操作发生时采集ADB环境信息,结合事件本身特征,提高识别自动化操作的准确性。
通过RUM数据分析识别异常操作
将上述检测结果通过RUM SDK上报后,可以在可观测平台中通过自定义查询进行分析。以下是几个典型的分析场景。
场景一:识别开启无障碍服务的用户
通过分析无障碍服务的开启状态和服务ID,快速定位可疑用户。
-- 查询最近1小时内开启无障碍服务的用户及其操作次数
* and context.accessibility_enabled: true |
SELECT
“user.name“,
“context.accessibility_service_id“,
COUNT(*) as operation_count,
COUNT(DISTINCT “session.id“) as session_count
FROM log
GROUP BY
“user.name“, “context.accessibility_service_id“
ORDER BY
operation_count DESC
LIMIT 100
分析说明:如果某个用户的操作次数异常高,且开启了非系统自带的陌生无障碍服务,则需要重点关注。
场景二:识别具备全控能力的无障碍服务
重点分析那些同时具备读取屏幕和操作应用能力的服务。
-- 查询具备全控能力的无障碍服务及影响的用户数
* and context.can_retrieve_window: true and context.can_perform_gestures: true |
SELECT
“context.accessibility_service_id“,
COUNT(DISTINCT “user.name“) as affected_users,
COUNT(DISTINCT “device.id“) as affected_devices
FROM log
GROUP BY “context.accessibility_service_id“
ORDER BY affected_users DESC
分析说明:同时具备屏幕读取和手势操作能力的服务,被用于自动化操作的风险更高。
场景三:识别ADB连接环境下的操作
分析在ADB连接状态下的用户操作,这些操作很可能来自脚本。
-- 查询 ADB 开启状态下的用户操作特征
* and (context.adb_enabled: true or context.usb_adb_active:true or context.adb_port_open: true) |
SELECT
“user.name“,
“device.id“,
CASE
WHEN “context.adb_enabled“ = true THEN ‘ADB已开启‘
WHEN “context.usb_adb_active“ = true THEN ‘USB-ADB已连接‘
WHEN “context.adb_port_open“ = true THEN ‘ADB端口已打开‘
END as adb_status,
COUNT(*) as event_count
FROM log
GROUP BY “user.name“, “device.id“, adb_status
ORDER BY event_count DESC
LIMIT 100
分析说明:普通用户手机长期开启或连接ADB的情况较少见,此类环境下的高频操作很可疑。
场景四:识别注入事件特征
通过事件标志位和属性缺失情况,识别可能通过 INJECT_EVENTS 注入的事件。
-- 查询具有注入特征的事件
* and event_type: “action“ and (
context.is_generated_gesture: true
or context.event_source = ‘SOURCE_UNKNOWN‘
or (context.has_pressure: false and context.has_size: false and context.has_tool_type: false)
) |
SELECT
“user.name“,
“device.id“,
COUNT(*) as injected_event_count,
COUNT(DISTINCT session_id) as session_count,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY “user.name“), 2) as inject_ratio
FROM log
GROUP BY “user.name“, “device.id“
HAVING injected_event_count > 50
ORDER BY inject_ratio DESC
LIMIT 100
分析说明:如果某个用户的事件中,被标记为“疑似注入”的事件比例过高(例如超过50%),则存在非人操作的可能性极大。
结语
本文对AI Agent或脚本自动化操作Android手机的主流技术原理进行了拆解,并详细介绍了针对三种技术路径的检测思路与代码实践。AutoGLM、豆包手机助手等AI Agent的兴起,标志着移动端交互正迈向新阶段。AI能自动完成复杂任务无疑是技术进步,但也为数据分析和业务风控带来了识别“非人”操作的新挑战。
实现精准的“非人”操作识别,绝非依赖单一维度即可完成。它需要一个立体的检测体系,涵盖应用运行环境、无障碍服务特征、设备指纹、用户行为序列、屏幕操作轨迹等多维度信息的交叉分析与建模。当前,一些可观测性平台的体验监控SDK已经开始采集相关属性,基于这些属性,开发者可以自定义分析规则,更有效地甄别潜在的非人操作,守护业务数据的纯净与可靠。