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
@@ -18,6 +18,13 @@ from .._core import Stub, AndroidOnlyError, _app_data_dir
|
||||
|
||||
from ..java.lang import RuntimeException
|
||||
|
||||
# Linux 真实实现导入
|
||||
try:
|
||||
from .._linux_impl import IS_LINUX, LinuxPower, LinuxProcess, HAS_PSUTIL as _HAS_PSUTIL
|
||||
_USE_LINUX = IS_LINUX
|
||||
except ImportError:
|
||||
_USE_LINUX = False
|
||||
|
||||
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
||||
|
||||
|
||||
@@ -505,6 +512,54 @@ class BatteryManager(Stub):
|
||||
self._discharge_time_remaining = -1
|
||||
self._is_capacity_estimated = False
|
||||
self._capacity_percentage = 100.0
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
|
||||
_LINUX_STATUS_MAP = {
|
||||
"charging": BATTERY_STATUS_CHARGING,
|
||||
"discharging": BATTERY_STATUS_DISCHARGING,
|
||||
"not charging": BATTERY_STATUS_NOT_CHARGING,
|
||||
"full": BATTERY_STATUS_FULL,
|
||||
"unknown": 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._battery_level = int(level)
|
||||
self._capacity_percentage = float(level)
|
||||
# BATTERY_PROPERTY_CAPACITY 在 Android 中返回百分比 (0-100)
|
||||
self._capacity = int(level)
|
||||
else:
|
||||
# 无电池或电量未知
|
||||
self._battery_level = -1
|
||||
self._capacity_percentage = 0.0
|
||||
self._capacity = -1
|
||||
status_str = str(info.get("status", "unknown")).lower()
|
||||
self._status = self._LINUX_STATUS_MAP.get(status_str, BATTERY_STATUS_UNKNOWN)
|
||||
voltage = info.get("voltage", 0)
|
||||
if isinstance(voltage, (int, float)):
|
||||
self._voltage = int(voltage * 1000) # V -> mV
|
||||
temperature = info.get("temperature", 0)
|
||||
if isinstance(temperature, (int, float)):
|
||||
self._temperature = temperature # 摄氏度
|
||||
self._technology = info.get("technology", "") or "Li-ion"
|
||||
if self._status == BATTERY_STATUS_CHARGING:
|
||||
self._plugged = BATTERY_PLUGGED_AC
|
||||
else:
|
||||
self._plugged = 0
|
||||
|
||||
@staticmethod
|
||||
def getInstance(context: Any) -> "BatteryManager":
|
||||
@@ -513,6 +568,8 @@ class BatteryManager(Stub):
|
||||
return BatteryManager._instance
|
||||
|
||||
def getIntProperty(self, id: int) -> int:
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
properties = {
|
||||
BATTERY_PROPERTY_CAPACITY: self._capacity,
|
||||
BATTERY_PROPERTY_CHARGE_COUNTER: self._charge_counter,
|
||||
@@ -521,6 +578,9 @@ class BatteryManager(Stub):
|
||||
BATTERY_PROPERTY_ENERGY_COUNTER: self._energy_counter,
|
||||
BATTERY_PROPERTY_CHARGE_TIME_REMAINING: self._charge_time_remaining,
|
||||
BATTERY_PROPERTY_DISCHARGE_TIME_REMAINING: self._discharge_time_remaining,
|
||||
BATTERY_PROPERTY_STATUS: self._status,
|
||||
BATTERY_PROPERTY_TEMPERATURE: int(self._temperature),
|
||||
BATTERY_PROPERTY_VOLTAGE: self._voltage,
|
||||
}
|
||||
return properties.get(id, 0)
|
||||
|
||||
@@ -528,9 +588,13 @@ class BatteryManager(Stub):
|
||||
return self.getIntProperty(id)
|
||||
|
||||
def isCharging(self) -> bool:
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._status == BATTERY_STATUS_CHARGING or self._status == BATTERY_STATUS_FULL
|
||||
|
||||
def getBatteryLevel(self) -> int:
|
||||
if _USE_LINUX:
|
||||
self._refresh_from_linux()
|
||||
return self._battery_level
|
||||
|
||||
def getInt(intent: Any, key: str, defaultValue: int) -> int:
|
||||
@@ -687,9 +751,11 @@ class PowerManager(Stub):
|
||||
self._shutdown_requested = False
|
||||
self._thermal_status = THERMAL_STATUS_NONE
|
||||
self._power_save_state = PowerSaveState()
|
||||
self._current_wake_up_reason = WAKE_REASON_UNKNOWN
|
||||
self._current_wake_up_reason = PowerManager.WAKE_REASON_UNKNOWN
|
||||
|
||||
def isInteractive(self) -> bool:
|
||||
if _USE_LINUX:
|
||||
return self.isScreenOn()
|
||||
return self._is_interactive
|
||||
|
||||
def isPowerSaveMode(self) -> bool:
|
||||
@@ -705,6 +771,13 @@ class PowerManager(Stub):
|
||||
return True
|
||||
|
||||
def isScreenOn(self) -> bool:
|
||||
if _USE_LINUX:
|
||||
import os as _os_mod
|
||||
display = _os_mod.environ.get("DISPLAY", "")
|
||||
if display:
|
||||
return True
|
||||
wayland = _os_mod.environ.get("WAYLAND_DISPLAY", "")
|
||||
return bool(wayland)
|
||||
return self._is_screen_on
|
||||
|
||||
def isWakeLockLevelSupported(self, level: int) -> bool:
|
||||
|
||||
Reference in new issue
Block a user