feat: publish workphone SDK deployment and API docs
This commit is contained in:
207
sdk/agent/anti_ban/sensor_simulator.py
Normal file
207
sdk/agent/anti_ban/sensor_simulator.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
传感器模拟 — 伪造加速度计/陀螺仪数据,模拟真人手持特征
|
||||
|
||||
功能:
|
||||
1. 加速度计噪声注入 (模拟手持微抖)
|
||||
2. 陀螺仪数据生成 (缓慢旋转漂移)
|
||||
3. 传感器事件写入 /dev/input/ (需 root)
|
||||
4. 非 root 降级为日志记录
|
||||
5. 与 DeviceGuard 联动: 若传感器数 < 5 则触发补偿
|
||||
"""
|
||||
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import random
|
||||
import struct
|
||||
import time
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SensorSimulator:
|
||||
"""传感器模拟器 — 让设备看起来像被人拿着"""
|
||||
|
||||
GRAVITY = 9.81
|
||||
INPUT_EVENT_FORMAT = "llHHi" # struct input_event: sec, usec, type, code, value
|
||||
|
||||
def __init__(self, device=None, has_root: bool = False):
|
||||
"""
|
||||
Args:
|
||||
device: uiautomator2 设备对象
|
||||
has_root: 是否有 root 权限
|
||||
"""
|
||||
self.d = device
|
||||
self.has_root = has_root
|
||||
self._running = False
|
||||
self._base_orientation = self._random_orientation()
|
||||
|
||||
def generate_accelerometer_sample(self) -> Dict[str, float]:
|
||||
"""
|
||||
生成一个加速度计采样值 — 模拟静止手持状态
|
||||
|
||||
真实手持特征:
|
||||
- x 轴: 微小随机漂移 (+-0.3 m/s²)
|
||||
- y 轴: 接近 0 (横持) 或接近 gravity (竖持)
|
||||
- z 轴: 接近 gravity (平放) 或接近 0 (竖持)
|
||||
"""
|
||||
ox, oy, oz = self._base_orientation
|
||||
|
||||
noise_x = random.gauss(0, 0.15)
|
||||
noise_y = random.gauss(0, 0.15)
|
||||
noise_z = random.gauss(0, 0.10)
|
||||
|
||||
drift = random.gauss(0, 0.02)
|
||||
self._base_orientation = (
|
||||
ox + drift * random.choice([-1, 1]),
|
||||
oy + drift * random.choice([-1, 1]),
|
||||
oz + drift * random.choice([-1, 1]),
|
||||
)
|
||||
|
||||
return {
|
||||
"x": round(ox + noise_x, 4),
|
||||
"y": round(oy + noise_y, 4),
|
||||
"z": round(oz + noise_z, 4),
|
||||
"timestamp": time.time(),
|
||||
}
|
||||
|
||||
def generate_gyroscope_sample(self) -> Dict[str, float]:
|
||||
"""
|
||||
生成一个陀螺仪采样值 — 模拟静止时的微旋转
|
||||
|
||||
真实特征: 各轴接近 0,偶尔有微小角速度 (rad/s)
|
||||
"""
|
||||
return {
|
||||
"x": round(random.gauss(0, 0.005), 6),
|
||||
"y": round(random.gauss(0, 0.005), 6),
|
||||
"z": round(random.gauss(0, 0.003), 6),
|
||||
"timestamp": time.time(),
|
||||
}
|
||||
|
||||
def generate_batch(self, count: int = 10, interval_ms: int = 20) -> List[dict]:
|
||||
"""生成一批传感器数据 (用于上报或写入)"""
|
||||
samples = []
|
||||
for _ in range(count):
|
||||
samples.append({
|
||||
"accel": self.generate_accelerometer_sample(),
|
||||
"gyro": self.generate_gyroscope_sample(),
|
||||
})
|
||||
time.sleep(interval_ms / 1000)
|
||||
return samples
|
||||
|
||||
def inject_to_device(self, duration_sec: float = 5.0, freq_hz: int = 50):
|
||||
"""
|
||||
向 /dev/input/ 写入伪造传感器事件 (需 root)。
|
||||
非 root 环境降级为日志输出。
|
||||
"""
|
||||
if not self.has_root:
|
||||
logger.info("非 root,传感器模拟降级为日志记录")
|
||||
self._simulate_log_only(duration_sec, freq_hz)
|
||||
return
|
||||
|
||||
input_dev = self._find_sensor_input_device()
|
||||
if not input_dev:
|
||||
logger.warning("未找到传感器 input 设备,降级为日志")
|
||||
self._simulate_log_only(duration_sec, freq_hz)
|
||||
return
|
||||
|
||||
logger.info(f"向 {input_dev} 注入传感器数据 {duration_sec}s @ {freq_hz}Hz")
|
||||
interval = 1.0 / freq_hz
|
||||
end_time = time.time() + duration_sec
|
||||
count = 0
|
||||
|
||||
try:
|
||||
while time.time() < end_time:
|
||||
sample = self.generate_accelerometer_sample()
|
||||
self._write_input_event(input_dev, sample)
|
||||
time.sleep(interval)
|
||||
count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"传感器注入中断: {e}")
|
||||
|
||||
logger.info(f"传感器注入完成,写入 {count} 个事件")
|
||||
|
||||
def get_device_sensor_count(self) -> int:
|
||||
"""获取设备实际传感器数量"""
|
||||
if not self.d:
|
||||
return -1
|
||||
try:
|
||||
out = self.d.shell(
|
||||
"dumpsys sensorservice 2>/dev/null | grep -c 'Sensor'"
|
||||
).output.strip()
|
||||
return int(out)
|
||||
except Exception:
|
||||
return -1
|
||||
|
||||
def check_sensor_health(self) -> dict:
|
||||
"""检查传感器环境是否正常"""
|
||||
count = self.get_device_sensor_count()
|
||||
result = {
|
||||
"sensor_count": count,
|
||||
"looks_real": count >= 5,
|
||||
"needs_compensation": count < 5 and count >= 0,
|
||||
}
|
||||
if result["needs_compensation"]:
|
||||
result["warning"] = f"传感器数量偏少({count}), 疑似模拟器特征"
|
||||
return result
|
||||
|
||||
# ---- 内部方法 ----
|
||||
|
||||
def _random_orientation(self) -> Tuple[float, float, float]:
|
||||
"""随机选择一个初始手持姿态"""
|
||||
patterns = [
|
||||
(0.3, 0.5, self.GRAVITY - 0.2), # 近似平放
|
||||
(0.2, self.GRAVITY * 0.7, self.GRAVITY * 0.7), # 竖持 ~45°
|
||||
(0.1, self.GRAVITY - 0.3, 1.0), # 接近竖持
|
||||
]
|
||||
base = random.choice(patterns)
|
||||
return tuple(v + random.gauss(0, 0.1) for v in base)
|
||||
|
||||
def _find_sensor_input_device(self) -> Optional[str]:
|
||||
"""查找传感器对应的 input 设备节点"""
|
||||
if not self.d:
|
||||
return None
|
||||
try:
|
||||
out = self.d.shell("ls /dev/input/event* 2>/dev/null").output.strip()
|
||||
devices = out.split()
|
||||
for dev in devices:
|
||||
info = self.d.shell(f"cat /proc/bus/input/devices 2>/dev/null").output
|
||||
if "accelerometer" in info.lower() or "accel" in info.lower():
|
||||
return dev
|
||||
return devices[0] if devices else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _write_input_event(self, device_path: str, sample: dict):
|
||||
"""写入一个 input_event 到设备节点"""
|
||||
if not self.d:
|
||||
return
|
||||
try:
|
||||
ts = sample["timestamp"]
|
||||
sec = int(ts)
|
||||
usec = int((ts - sec) * 1_000_000)
|
||||
x_val = int(sample["x"] * 1000)
|
||||
cmd = (
|
||||
f"echo -ne '\\x{sec & 0xff:02x}\\x{(sec >> 8) & 0xff:02x}' "
|
||||
f"> {device_path}"
|
||||
)
|
||||
self.d.shell(cmd)
|
||||
except Exception as e:
|
||||
logger.debug(f"input_event 写入失败: {e}")
|
||||
|
||||
def _simulate_log_only(self, duration_sec: float, freq_hz: int):
|
||||
"""非 root 降级: 仅记录日志"""
|
||||
interval = 1.0 / freq_hz
|
||||
end_time = time.time() + duration_sec
|
||||
count = 0
|
||||
while time.time() < end_time:
|
||||
accel = self.generate_accelerometer_sample()
|
||||
gyro = self.generate_gyroscope_sample()
|
||||
if count % (freq_hz * 2) == 0:
|
||||
logger.debug(
|
||||
f"sensor[log] accel=({accel['x']:.2f},{accel['y']:.2f},{accel['z']:.2f}) "
|
||||
f"gyro=({gyro['x']:.4f},{gyro['y']:.4f},{gyro['z']:.4f})"
|
||||
)
|
||||
time.sleep(interval)
|
||||
count += 1
|
||||
Reference in New Issue
Block a user