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 - 真实进程列表
This commit is contained in:
1 parent
8c3bd3c415
commit
690022cea3
17 files changed
+2083
-8
No files matched your search
@@ -20,6 +20,13 @@ from .._core import (
|
||||
HAS_PSUTIL,
|
||||
)
|
||||
|
||||
# Linux 真实实现导入
|
||||
try:
|
||||
from .._linux_impl import IS_LINUX, LinuxPower, LinuxProcess, HAS_PSUTIL as _HAS_PSUTIL
|
||||
_USE_LINUX = IS_LINUX
|
||||
except ImportError:
|
||||
_USE_LINUX = False
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Build
|
||||
@@ -118,6 +125,50 @@ class Build(Stub):
|
||||
def getFingerprint() -> str:
|
||||
return Build.FINGERPRINT
|
||||
|
||||
@classmethod
|
||||
def _populate_from_linux(cls):
|
||||
"""从 LinuxProcess.get_system_info() 填充真实设备信息"""
|
||||
try:
|
||||
info = LinuxProcess.get_system_info() or {}
|
||||
import platform as _platform
|
||||
import socket as _socket
|
||||
hostname = info.get("hostname") or _socket.gethostname()
|
||||
system = info.get("platform") or _platform.system()
|
||||
arch = info.get("arch") or _platform.machine()
|
||||
kernel = info.get("kernel") or _platform.release()
|
||||
cls.MODEL = hostname
|
||||
cls.MANUFACTURER = "Linux"
|
||||
cls.BRAND = system
|
||||
cls.DEVICE = arch
|
||||
cls.BOARD = arch
|
||||
cls.HARDWARE = arch
|
||||
cls.HOST = hostname
|
||||
cls.PRODUCT = hostname
|
||||
cls.DISPLAY = kernel
|
||||
cls.ID = kernel
|
||||
# FINGERPRINT: brand/device/device:version/id/build:user/release-keys
|
||||
cls.FINGERPRINT = (
|
||||
f"{system}/{hostname}/{arch}:Linux/{kernel}/{hostname}:user/release-keys"
|
||||
)
|
||||
# CPU ABI 用架构映射
|
||||
_abi_map = {
|
||||
"x86_64": "x86_64", "amd64": "x86_64",
|
||||
"aarch64": "arm64-v8a", "arm64": "arm64-v8a",
|
||||
"x86": "x86", "i386": "x86", "i686": "x86",
|
||||
"armv7l": "armeabi-v7a", "arm": "armeabi-v7a",
|
||||
}
|
||||
cls.CPU_ABI = _abi_map.get(arch, arch)
|
||||
cls.SUPPORTED_ABIS = [cls.CPU_ABI]
|
||||
cls.SUPPORTED_64_BIT_ABIS = [cls.CPU_ABI] if "64" in cls.CPU_ABI else []
|
||||
cls.SUPPORTED_32_BIT_ABIS = [cls.CPU_ABI] if "64" not in cls.CPU_ABI else []
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# 在 Linux 上用真实系统信息填充 Build
|
||||
if _USE_LINUX:
|
||||
Build._populate_from_linux()
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# SystemClock
|
||||
@@ -1299,6 +1350,20 @@ class PowerManager(Stub):
|
||||
return PowerManagerWakeLock(level_and_flags, tag)
|
||||
|
||||
def isInteractive(self) -> bool:
|
||||
if _USE_LINUX:
|
||||
return self.isScreenOn()
|
||||
return self._is_interactive
|
||||
|
||||
def isScreenOn(self) -> bool:
|
||||
"""屏幕是否点亮 — 在 Linux 上检查 DISPLAY 环境变量"""
|
||||
if _USE_LINUX:
|
||||
import os as _os_mod
|
||||
display = _os_mod.environ.get("DISPLAY", "")
|
||||
if display:
|
||||
return True
|
||||
# WAYLAND_DISPLAY 作为回退
|
||||
wayland = _os_mod.environ.get("WAYLAND_DISPLAY", "")
|
||||
return bool(wayland)
|
||||
return self._is_interactive
|
||||
|
||||
def isPowerSaveMode(self) -> bool:
|
||||
@@ -1478,8 +1543,58 @@ class BatteryManager(Stub):
|
||||
self._current_now = -500000
|
||||
self._current_avg = -450000
|
||||
self._energy_counter = 15000000
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
|
||||
_LINUX_STATUS_MAP = {
|
||||
"charging": 2, # BATTERY_STATUS_CHARGING
|
||||
"discharging": 3, # BATTERY_STATUS_DISCHARGING
|
||||
"not charging": 4, # BATTERY_STATUS_NOT_CHARGING
|
||||
"full": 5, # BATTERY_STATUS_FULL
|
||||
"unknown": 1, # BATTERY_STATUS_UNKNOWN
|
||||
}
|
||||
|
||||
def _get_linux_battery_info(self) -> dict:
|
||||
"""从 LinuxPower 获取真实电池信息"""
|
||||
try:
|
||||
return LinuxPower.get_battery_info() or {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def _refresh_from_linux(self):
|
||||
"""用 Linux 真实电池信息刷新内部状态"""
|
||||
info = self._get_linux_battery_info()
|
||||
if not info:
|
||||
return
|
||||
self._present = bool(info.get("present", False))
|
||||
level = info.get("level", -1)
|
||||
if self._present and isinstance(level, (int, float)) and level >= 0:
|
||||
self._level = int(level)
|
||||
self._capacity = int(level)
|
||||
else:
|
||||
# 无电池或电量未知
|
||||
self._level = -1
|
||||
self._capacity = -1
|
||||
status_str = str(info.get("status", "unknown")).lower()
|
||||
self._status = self._LINUX_STATUS_MAP.get(status_str, self.BATTERY_STATUS_UNKNOWN)
|
||||
voltage = info.get("voltage", 0)
|
||||
if isinstance(voltage, (int, float)):
|
||||
# LinuxPower 返回伏特, Android 期望毫伏
|
||||
self._voltage = int(voltage * 1000)
|
||||
temperature = info.get("temperature", 0)
|
||||
if isinstance(temperature, (int, float)):
|
||||
# LinuxPower 返回摄氏度, Android 期望十分之一度
|
||||
self._temperature = int(temperature * 10)
|
||||
self._technology = info.get("technology", "") or "Li-ion"
|
||||
# 充电状态决定 plugged
|
||||
if self._status == self.BATTERY_STATUS_CHARGING:
|
||||
self._plugged = self.BATTERY_PLUGGED_AC
|
||||
else:
|
||||
self._plugged = 0
|
||||
|
||||
def getIntProperty(self, prop_id: int) -> int:
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
if prop_id == self.BATTERY_PROPERTY_CAPACITY:
|
||||
return self._capacity
|
||||
if prop_id == self.BATTERY_PROPERTY_STATUS:
|
||||
@@ -1498,9 +1613,57 @@ class BatteryManager(Stub):
|
||||
return self.getIntProperty(prop_id)
|
||||
|
||||
def isCharging(self) -> bool:
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._status == self.BATTERY_STATUS_CHARGING
|
||||
|
||||
def isPresent(self) -> bool:
|
||||
"""电池是否存在"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._present
|
||||
|
||||
def getLevel(self) -> int:
|
||||
"""电池电量 (0-100, -1 表示未知)"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._level
|
||||
|
||||
def getVoltage(self) -> int:
|
||||
"""电池电压 (毫伏)"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._voltage
|
||||
|
||||
def getTemperature(self) -> int:
|
||||
"""电池温度 (十分之一摄氏度)"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._temperature
|
||||
|
||||
def getTechnology(self) -> str:
|
||||
"""电池技术"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._technology
|
||||
|
||||
def getPlugType(self) -> int:
|
||||
"""充电类型"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._plugged
|
||||
|
||||
def getStatus(self) -> int:
|
||||
"""电池状态"""
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._status
|
||||
|
||||
def computeChargeTimeRemaining(self) -> int:
|
||||
if _USE_LINUX:
|
||||
# 无电池或已充满时返回 -1
|
||||
if not self._present or self._status == self.BATTERY_STATUS_FULL:
|
||||
return -1
|
||||
return 1800000 # 30 minutes
|
||||
|
||||
def toString(self) -> str:
|
||||
|
||||
Reference in new issue
Block a user