Files
Android-pyApi/android_pyapi/android/hardware.py
T
shuiyu1123 690022cea3 feat: Linux 真实实现层 — 从存根库变为可用生产力工具
新增 _linux_impl.py (800+行):
- LinuxNetwork: 真实IP/接口/DNS/网关/MAC (ip命令+/proc/net)
- LinuxBluetooth: 真实蓝牙状态/扫描/配对/连接 (bluetoothctl)
- LinuxSensors: 真实温度/CPU频率/风扇 (/sys/class/hwmon)
- LinuxAudio: 真实音量控制/静音 (amixer)
- LinuxClipboard: 真实剪贴板读写 (xclip+tkinter回退)
- LinuxPower: 真实CPU/内存/电池 (psutil+/sys/class/power_supply)
- LinuxLocation: 真实IP地理定位 (ip-api.com)
- LinuxNotify: 真实桌面通知 (notify-send)
- LinuxDisplay: 真实屏幕尺寸/截屏/鼠标位置 (xdotool+X11)
- LinuxUIAutomation: 真实点击/输入/按键/滚动 (xdotool)
- LinuxCamera: 真实摄像头拍照 (opencv/V4L2)
- LinuxProcess: 真实进程列表/系统信息/应用管理 (psutil+/proc)
- LinuxShell: 真实命令执行 (subprocess)

改造10+核心类使用Linux真实实现:
- BluetoothAdapter/SensorManager/AudioManager
- ConnectivityManager/WifiManager/TelephonyManager
- LocationManager/PowerManager/BatteryManager/Build
- ActivityManager/Activity/Toast/Display/ClipboardManager
- NotificationManager

15项真实行为测试全部通过:
- 真实IP: 10.60.218.244
- 真实位置: 北京 39.9042, 116.407
- 真实主机名: work-41-vci-g427c
- 真实内核: 5.15.120.byteatom-ck.14
- 真实内存: 1.8GB/3.8GB
- 真实进程列表
2026-06-24 15:40:49 +00:00

615 lines
24 KiB
Python

"""
Android Hardware 组件 — Sensor, SensorManager, SensorEvent, 等
方法签名完全对齐 Android SDK
"""
import math
import time
import random
from typing import Any, Callable, Dict, List, Optional, Tuple
from .._core import Stub, AndroidOnlyError, android_context, HAS_PIL
# Linux 真实实现导入
try:
from .._linux_impl import IS_LINUX, LinuxBluetooth, LinuxSensors, HAS_BLUETOOTHCTL
_USE_LINUX = IS_LINUX
except ImportError:
_USE_LINUX = False
# ═══════════════════════════════════════════════════════════════
# Sensor
# ═══════════════════════════════════════════════════════════════
class Sensor(Stub):
"""Sensor — 传感器"""
TYPE_ACCELEROMETER = 1
TYPE_MAGNETIC_FIELD = 2
TYPE_ORIENTATION = 3
TYPE_GYROSCOPE = 4
TYPE_LIGHT = 5
TYPE_PRESSURE = 6
TYPE_PROXIMITY = 8
TYPE_GRAVITY = 9
TYPE_LINEAR_ACCELERATION = 10
TYPE_ROTATION_VECTOR = 11
TYPE_RELATIVE_HUMIDITY = 12
TYPE_AMBIENT_TEMPERATURE = 13
TYPE_STEP_COUNTER = 19
TYPE_STEP_DETECTOR = 18
TYPE_HEART_RATE = 21
TYPE_LOW_LATENCY_OFFBODY_DETECT = 34
TYPE_ACCELEROMETER_LIMITED_AXES = 35
TYPE_DEVICE_ORIENTATION = 27
TYPE_STATIONARY_DETECT = 28
REPORTING_MODE_CONTINUOUS = 0
REPORTING_MODE_ON_CHANGE = 1
REPORTING_MODE_ONE_SHOT = 2
REPORTING_MODE_SPECIAL_TRIGGER = 3
def __init__(self, type_: int, name: str = "Simulated Sensor",
vendor: str = "Android-pyApi", version: int = 1,
resolution: float = 1.0, max_range: float = 100.0,
power: float = 0.5, min_delay: int = 10000):
super().__init__()
self._type = type_
self._name = name
self._vendor = vendor
self._version = version
self._resolution = resolution
self._max_range = max_range
self._power = power
self._min_delay = min_delay
# Assign default values based on sensor type
type_defaults = {
Sensor.TYPE_ACCELEROMETER: ("Accelerometer", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_GYROSCOPE: ("Gyroscope", 0.001, 34.91, 6.1, 10000),
Sensor.TYPE_MAGNETIC_FIELD: ("Magnetic Field", 0.01, 2000.0, 4.0, 10000),
Sensor.TYPE_LIGHT: ("Light", 1.0, 40000.0, 0.5, 10000),
Sensor.TYPE_PROXIMITY: ("Proximity", 1.0, 10.0, 0.5, 10000),
Sensor.TYPE_PRESSURE: ("Pressure", 0.01, 1100.0, 1.0, 10000),
Sensor.TYPE_GRAVITY: ("Gravity", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_LINEAR_ACCELERATION: ("Linear Acceleration", 0.01, 39.24, 3.0, 10000),
Sensor.TYPE_ROTATION_VECTOR: ("Rotation Vector", 0.0001, 1.0, 5.0, 10000),
Sensor.TYPE_RELATIVE_HUMIDITY: ("Relative Humidity", 0.1, 100.0, 0.5, 10000),
Sensor.TYPE_AMBIENT_TEMPERATURE: ("Ambient Temperature", 0.01, 80.0, 0.5, 10000),
Sensor.TYPE_STEP_COUNTER: ("Step Counter", 1.0, 100000.0, 0.1, 100000),
Sensor.TYPE_STEP_DETECTOR: ("Step Detector", 1.0, 1.0, 0.1, 100000),
Sensor.TYPE_HEART_RATE: ("Heart Rate", 1.0, 220.0, 0.5, 100000),
}
if type_ in type_defaults:
n, res, mr, p, md = type_defaults[type_]
self._name = name if name != "Simulated Sensor" else n
self._resolution = resolution if resolution != 1.0 else res
self._max_range = max_range if max_range != 100.0 else mr
self._power = power if power != 0.5 else p
self._min_delay = min_delay if min_delay != 10000 else md
def getType(self) -> int:
return self._type
def getName(self) -> str:
return self._name
def getVendor(self) -> str:
return self._vendor
def getVersion(self) -> int:
return self._version
def getMaximumRange(self) -> float:
return self._max_range
def getMinDelay(self) -> int:
return self._min_delay
def getPower(self) -> float:
return self._power
def getResolution(self) -> float:
return self._resolution
def getFifoMaxEventCount(self) -> int:
return 0
def getFifoReservedEventCount(self) -> int:
return 0
def getReportingMode(self) -> int:
return Sensor.REPORTING_MODE_CONTINUOUS
def isWakeUpSensor(self) -> bool:
return False
def getStringType(self) -> str:
type_names = {
Sensor.TYPE_ACCELEROMETER: "android.sensor.accelerometer",
Sensor.TYPE_MAGNETIC_FIELD: "android.sensor.magnetic_field",
Sensor.TYPE_GYROSCOPE: "android.sensor.gyroscope",
Sensor.TYPE_LIGHT: "android.sensor.light",
Sensor.TYPE_PRESSURE: "android.sensor.pressure",
Sensor.TYPE_PROXIMITY: "android.sensor.proximity",
Sensor.TYPE_GRAVITY: "android.sensor.gravity",
Sensor.TYPE_LINEAR_ACCELERATION: "android.sensor.linear_acceleration",
Sensor.TYPE_ROTATION_VECTOR: "android.sensor.rotation_vector",
Sensor.TYPE_RELATIVE_HUMIDITY: "android.sensor.relative_humidity",
Sensor.TYPE_AMBIENT_TEMPERATURE: "android.sensor.ambient_temperature",
Sensor.TYPE_STEP_COUNTER: "android.sensor.step_counter",
Sensor.TYPE_STEP_DETECTOR: "android.sensor.step_detector",
Sensor.TYPE_HEART_RATE: "android.sensor.heart_rate",
}
return type_names.get(self._type, "android.sensor.unknown")
def toString(self) -> str:
return f"{self._name} (type={self._type}, vendor={self._vendor}, ver={self._version})"
# ═══════════════════════════════════════════════════════════════
# SensorEvent
# ═══════════════════════════════════════════════════════════════
class SensorEvent(Stub):
"""SensorEvent — 传感器事件"""
def __init__(self, sensor: Sensor, accuracy: int = 3):
super().__init__()
self.sensor = sensor
self.accuracy = accuracy
self.timestamp = int(time.time() * 1_000_000_000) # nanoseconds
self.values: List[float] = []
# Generate appropriate values based on sensor type
if sensor.getType() == Sensor.TYPE_ACCELEROMETER:
self.values = [random.uniform(-1, 1), random.uniform(-1, 1),
9.8 + random.uniform(-0.5, 0.5)]
elif sensor.getType() == Sensor.TYPE_GYROSCOPE:
self.values = [random.uniform(-0.1, 0.1)] * 3
elif sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD:
self.values = [random.uniform(-50, 50)] * 3
elif sensor.getType() == Sensor.TYPE_LIGHT:
self.values = [random.uniform(100, 1000)]
elif sensor.getType() == Sensor.TYPE_PROXIMITY:
self.values = [5.0 if random.random() > 0.5 else 0.0]
elif sensor.getType() == Sensor.TYPE_PRESSURE:
self.values = [random.uniform(980, 1050)]
elif sensor.getType() in (Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION):
self.values = [random.uniform(-1, 1)] * 3
elif sensor.getType() == Sensor.TYPE_ROTATION_VECTOR:
self.values = [random.uniform(-1, 1)] * 5
elif sensor.getType() == Sensor.TYPE_RELATIVE_HUMIDITY:
self.values = [random.uniform(30, 70)]
elif sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE:
self.values = [random.uniform(15, 35)]
elif sensor.getType() == Sensor.TYPE_STEP_COUNTER:
self.values = [float(random.randint(0, 10000))]
elif sensor.getType() == Sensor.TYPE_STEP_DETECTOR:
self.values = [1.0 if random.random() > 0.95 else 0.0]
elif sensor.getType() == Sensor.TYPE_HEART_RATE:
self.values = [random.uniform(60, 100)]
else:
self.values = [0.0]
# ═══════════════════════════════════════════════════════════════
# SensorEventListener
# ═══════════════════════════════════════════════════════════════
class SensorEventListener(Stub):
"""SensorEventListener — 传感器事件监听器"""
def onSensorChanged(self, event: SensorEvent):
pass
def onAccuracyChanged(self, sensor: Sensor, accuracy: int):
pass
# ═══════════════════════════════════════════════════════════════
# SensorDelay
# ═══════════════════════════════════════════════════════════════
class SensorDelay(Stub):
"""SensorDelay — 传感器采样延迟常量"""
FASTEST = 0
GAME = 1
UI = 2
NORMAL = 3
# ═══════════════════════════════════════════════════════════════
# SensorManager
# ═══════════════════════════════════════════════════════════════
class SensorManager(Stub):
"""SensorManager — 传感器管理器"""
SENSOR_DELAY_FASTEST = 0
SENSOR_DELAY_GAME = 1
SENSOR_DELAY_UI = 2
SENSOR_DELAY_NORMAL = 3
AXIS_X = 1
AXIS_Y = 2
AXIS_Z = 3
AXIS_MINUS_X = 129
AXIS_MINUS_Y = 130
AXIS_MINUS_Z = 131
def __init__(self):
super().__init__()
self._sensors: Dict[int, Sensor] = {}
self._listeners: Dict[int, List[Tuple[SensorEventListener, Sensor, int]]] = {}
# Create default sensors
for type_ in [
Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE,
Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_LIGHT,
Sensor.TYPE_PROXIMITY, Sensor.TYPE_PRESSURE,
Sensor.TYPE_GRAVITY, Sensor.TYPE_LINEAR_ACCELERATION,
Sensor.TYPE_ROTATION_VECTOR, Sensor.TYPE_RELATIVE_HUMIDITY,
Sensor.TYPE_AMBIENT_TEMPERATURE, Sensor.TYPE_STEP_COUNTER,
Sensor.TYPE_STEP_DETECTOR, Sensor.TYPE_HEART_RATE,
]:
self._sensors[type_] = Sensor(type_)
def getDefaultSensor(self, type_: int) -> Optional[Sensor]:
if _USE_LINUX:
sensor = self._build_linux_sensor(type_)
if sensor is not None:
return sensor
return self._sensors.get(type_)
def _build_linux_sensor(self, type_: int) -> Optional[Sensor]:
"""根据 Linux 真实传感器数据构造 Sensor 对象"""
try:
if type_ == Sensor.TYPE_AMBIENT_TEMPERATURE:
temps = LinuxSensors.get_temperatures()
if temps:
t = temps[0]
return Sensor(Sensor.TYPE_AMBIENT_TEMPERATURE,
name=t.get("name", "Linux Temperature"),
vendor="Linux", resolution=0.01,
max_range=125.0, power=0.0, min_delay=1000)
return None
if type_ == Sensor.TYPE_LIGHT:
light = LinuxSensors.get_light_level()
if light is not None:
return Sensor(Sensor.TYPE_LIGHT, name="Linux Light Sensor",
vendor="Linux", resolution=0.001,
max_range=40000.0, power=0.0, min_delay=1000)
return None
except Exception:
return None
return None
def getSensorList(self, type_: int) -> List[Sensor]:
if _USE_LINUX:
return self._get_linux_sensor_list(type_)
if type_ == -1: # ALL
return list(self._sensors.values())
sensor = self._sensors.get(type_)
return [sensor] if sensor else []
def _get_linux_sensor_list(self, type_: int) -> List[Sensor]:
"""从 Linux 真实传感器数据生成传感器列表"""
sensors: List[Sensor] = []
try:
if type_ == -1 or type_ == Sensor.TYPE_AMBIENT_TEMPERATURE:
for t in LinuxSensors.get_temperatures():
sensors.append(Sensor(
Sensor.TYPE_AMBIENT_TEMPERATURE,
name=t.get("name", "Linux Temperature"),
vendor="Linux", resolution=0.01,
max_range=125.0, power=0.0, min_delay=1000))
if type_ == -1 or type_ == Sensor.TYPE_LIGHT:
light = LinuxSensors.get_light_level()
if light is not None:
sensors.append(Sensor(
Sensor.TYPE_LIGHT, name="Linux Light Sensor",
vendor="Linux", resolution=0.001,
max_range=40000.0, power=0.0, min_delay=1000))
# CPU 频率作为附加传感器 (映射到压力传感器类型以承载真实数据)
if type_ == -1 or type_ == Sensor.TYPE_PRESSURE:
for f in LinuxSensors.get_cpu_frequency():
sensors.append(Sensor(
Sensor.TYPE_PRESSURE,
name=f"CPU{f.get('cpu', 0)} Frequency",
vendor="Linux", resolution=1.0,
max_range=10000.0, power=0.0, min_delay=1000))
# 风扇转速作为附加传感器 (映射到相对湿度类型以承载真实数据)
if type_ == -1 or type_ == Sensor.TYPE_RELATIVE_HUMIDITY:
for fan in LinuxSensors.get_fan_speeds():
sensors.append(Sensor(
Sensor.TYPE_RELATIVE_HUMIDITY,
name=f"Fan {fan.get('name', '')}",
vendor="Linux", resolution=1.0,
max_range=10000.0, power=0.0, min_delay=1000))
except Exception:
pass
# 若未取到真实数据则回退到模拟传感器
if not sensors:
if type_ == -1:
sensors = list(self._sensors.values())
else:
sensor = self._sensors.get(type_)
if sensor:
sensors = [sensor]
return sensors
def getSensors(self) -> List[Sensor]:
return list(self._sensors.values())
def registerListener(self, listener: SensorEventListener, sensor: Sensor,
samplingPeriodUs: int, maxReportLatencyUs: int = 0) -> bool:
if sensor.getType() not in self._sensors:
return False
if listener not in self._listeners:
self._listeners[id(listener)] = []
self._listeners[id(listener)].append((listener, sensor, samplingPeriodUs))
return True
def unregisterListener(self, listener: SensorEventListener, sensor: Sensor = None) -> bool:
lid = id(listener)
if lid in self._listeners:
if sensor:
self._listeners[lid] = [
(l, s, d) for l, s, d in self._listeners[lid]
if s.getType() != sensor.getType()
]
if not self._listeners[lid]:
del self._listeners[lid]
else:
del self._listeners[lid]
return True
return False
@staticmethod
def getOrientation(R: List[float], values: List[float]) -> List[float]:
if len(R) >= 9 and len(values) >= 3:
values[0] = math.degrees(math.atan2(R[1], R[4])) # azimuth
values[1] = math.degrees(math.asin(-R[7])) # pitch
values[2] = math.degrees(math.atan2(-R[6], R[8])) # roll
return values
@staticmethod
def getRotationMatrix(R: List[float], I: List[float],
gravity: List[float], geomagnetic: List[float]) -> bool:
if len(R) < 9:
R.extend([0] * (9 - len(R)))
if len(R) >= 9:
R[0] = 1.0
R[1] = 0.0
R[2] = 0.0
R[3] = 0.0
R[4] = 1.0
R[5] = 0.0
R[6] = 0.0
R[7] = 0.0
R[8] = 1.0
return True
@staticmethod
def getAltitude(p0: float, p: float) -> float:
return 44330.0 * (1.0 - math.pow(p / p0, 1.0 / 5.255))
@staticmethod
def getInclination(I: List[float]) -> float:
return 0.0
@staticmethod
def remapCoordinateSystem(inR: List[float], X: int, Y: int,
outR: List[float]) -> bool:
if len(inR) >= 9 and len(outR) >= 9:
for i in range(9):
outR[i] = inR[i]
return True
def getDeviceId(self) -> str:
return "sensor_hub_0"
# ═══════════════════════════════════════════════════════════════
# HardwareBuffer
# ═══════════════════════════════════════════════════════════════
class HardwareBuffer(Stub):
"""HardwareBuffer — 硬件缓冲 (API 26+)"""
USAGE_CPU_READ_OFTEN = 0x00000003
USAGE_CPU_WRITE_OFTEN = 0x00000030
USAGE_GPU_SAMPLED_IMAGE = 0x00000100
USAGE_GPU_COLOR_OUTPUT = 0x00000200
USAGE_GPU_DATA_BUFFER = 0x00001000
USAGE_SENSOR_DIRECT_DATA = 0x00400000
USAGE_PROTECTED_CONTENT = 0x00004000
USAGE_VIDEO_ENCODE = 0x00010000
USAGE_GPU_FRAMEBUFFER = 0x00020000
USAGE_COMPOSER_OVERLAY = 0x00000800
USAGE_FRONT_BUFFER_QUEUE = 0x00008000
BLOB = 0x21
RGBA_8888 = 1
RGBA_FP16 = 22
RGBA_1010102 = 43
RGBX_8888 = 2
RGB_888 = 3
RGB_565 = 4
YCBCR_420_888 = 35
def __init__(self, width: int, height: int, format: int, layers: int,
usage: int, buffer_size: int = 0):
super().__init__()
self._width = width
self._height = height
self._format = format
self._layers = layers
self._usage = usage
self._buffer_size = buffer_size
self._closed = False
@property
def width(self) -> int:
return self._width
@property
def height(self) -> int:
return self._height
@property
def format(self) -> int:
return self._format
@property
def layers(self) -> int:
return self._layers
@property
def usage(self) -> int:
return self._usage
def destroy(self):
self.close()
def close(self):
self._closed = True
def isClosed(self) -> bool:
return self._closed
def isEmpty(self) -> bool:
return self._width == 0 or self._height == 0
def getWidth(self) -> int:
return self._width
def getHeight(self) -> int:
return self._height
def getFormat(self) -> int:
return self._format
def getLayers(self) -> int:
return self._layers
def getUsage(self) -> int:
return self._usage
def getStride(self) -> int:
return self._width
# ═══════════════════════════════════════════════════════════════
# BatteryManager (Constants)
# ═══════════════════════════════════════════════════════════════
class BatteryManager(Stub):
"""BatteryManager — 电池管理器常量 (实际实现在 android.os)"""
BATTERY_PROPERTY_CHARGE_COUNTER = 1
BATTERY_PROPERTY_CURRENT_NOW = 2
BATTERY_PROPERTY_CURRENT_AVERAGE = 3
BATTERY_PROPERTY_CAPACITY = 4
BATTERY_PROPERTY_STATUS = 5
BATTERY_PROPERTY_ENERGY_COUNTER = 6
BATTERY_STATUS_UNKNOWN = 1
BATTERY_STATUS_CHARGING = 2
BATTERY_STATUS_DISCHARGING = 3
BATTERY_STATUS_NOT_CHARGING = 4
BATTERY_STATUS_FULL = 5
BATTERY_HEALTH_UNKNOWN = 1
BATTERY_HEALTH_GOOD = 2
BATTERY_HEALTH_OVERHEAT = 3
BATTERY_HEALTH_DEAD = 4
BATTERY_HEALTH_OVER_VOLTAGE = 5
BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6
BATTERY_HEALTH_COLD = 7
BATTERY_PLUGGED_AC = 1
BATTERY_PLUGGED_USB = 2
BATTERY_PLUGGED_WIRELESS = 4
ACTION_CHARGING = "android.intent.action.ACTION_CHARGING"
ACTION_DISCHARGING = "android.intent.action.ACTION_DISCHARGING"
EXTRA_STATUS = "status"
EXTRA_HEALTH = "health"
EXTRA_LEVEL = "level"
EXTRA_SCALE = "scale"
EXTRA_PLUGGED = "plugged"
EXTRA_TEMPERATURE = "temperature"
EXTRA_VOLTAGE = "voltage"
EXTRA_TECHNOLOGY = "technology"
EXTRA_CHARGE_COUNTER = "charge_counter"
EXTRA_PRESENT = "present"
def __init__(self):
super().__init__()
def getIntProperty(self, id: int) -> int:
id_values = {
BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: 3500,
BatteryManager.BATTERY_PROPERTY_CAPACITY: 85,
BatteryManager.BATTERY_PROPERTY_STATUS: BatteryManager.BATTERY_STATUS_DISCHARGING,
}
return id_values.get(id, 0)
def getLongProperty(self, id: int) -> int:
return 0
# ═══════════════════════════════════════════════════════════════
# BiometricManager (lightweight)
# ═══════════════════════════════════════════════════════════════
class BiometricManager(Stub):
"""BiometricManager — 生物识别管理器 (轻量)"""
BIOMETRIC_SUCCESS = 0
BIOMETRIC_ERROR_HW_UNAVAILABLE = 1
BIOMETRIC_ERROR_NONE_ENROLLED = 11
BIOMETRIC_ERROR_NO_HARDWARE = 12
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15
def __init__(self):
super().__init__()
def canAuthenticate(self) -> int:
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
# ═══════════════════════════════════════════════════════════════
# Flashlight support
# ═══════════════════════════════════════════════════════════════
class CameraFlashlight(Stub):
"""CameraFlashlight — 手电筒支持"""
def __init__(self):
super().__init__()
self._on = False
def setTorchMode(self, cameraId: str, enabled: bool):
self._on = enabled
def isTorchModeEnabled(self, cameraId: str) -> bool:
return self._on
# ═══════════════════════════════════════════════════════════════
# __all__
# ═══════════════════════════════════════════════════════════════
__all__ = [
"Sensor", "SensorManager", "SensorEvent", "SensorEventListener",
"SensorDelay",
"HardwareBuffer",
"BatteryManager",
"BiometricManager",
"CameraFlashlight",
]