新增 _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 - 真实进程列表
1075 lines
34 KiB
Python
1075 lines
34 KiB
Python
"""
|
|
Android WiFi 组件 — WifiManager, WifiInfo, ScanResult 等
|
|
方法签名完全对齐 Android SDK
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import random
|
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
|
from datetime import datetime
|
|
|
|
from .._core import (
|
|
Stub, AndroidOnlyError, android_context,
|
|
resolve_android_path, to_android_path,
|
|
_sdcard_root, _app_data_dir, _package_name,
|
|
)
|
|
|
|
# Linux 真实实现导入
|
|
try:
|
|
from .._linux_impl import IS_LINUX, LinuxNetwork, HAS_IP
|
|
_USE_LINUX = IS_LINUX
|
|
except ImportError:
|
|
_USE_LINUX = False
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# SupplicantState
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class SupplicantState(Stub):
|
|
"""SupplicantState — wpa_supplicant 状态枚举"""
|
|
|
|
ASSOCIATED = "ASSOCIATED"
|
|
ASSOCIATING = "ASSOCIATING"
|
|
COMPLETED = "COMPLETED"
|
|
DISCONNECTED = "DISCONNECTED"
|
|
DORMANT = "DORMANT"
|
|
FOUR_WAY_HANDSHAKE = "FOUR_WAY_HANDSHAKE"
|
|
GROUP_HANDSHAKE = "GROUP_HANDSHAKE"
|
|
INACTIVE = "INACTIVE"
|
|
INTERFACE_DISABLED = "INTERFACE_DISABLED"
|
|
INVALID = "INVALID"
|
|
SCANNING = "SCANNING"
|
|
UNINITIALIZED = "UNINITIALIZED"
|
|
|
|
_VALID_STATES = [
|
|
"ASSOCIATED", "ASSOCIATING", "COMPLETED", "DISCONNECTED",
|
|
"DORMANT", "FOUR_WAY_HANDSHAKE", "GROUP_HANDSHAKE", "INACTIVE",
|
|
"INTERFACE_DISABLED", "INVALID", "SCANNING", "UNINITIALIZED",
|
|
]
|
|
|
|
def __init__(self, state: str = "DISCONNECTED"):
|
|
super().__init__()
|
|
self._state = state if state in self._VALID_STATES else "INVALID"
|
|
|
|
def getState(self) -> str:
|
|
return self._state
|
|
|
|
def toString(self) -> str:
|
|
return self._state
|
|
|
|
@staticmethod
|
|
def isValid(state_str: str) -> bool:
|
|
return state_str in SupplicantState._VALID_STATES
|
|
|
|
def compareTo(self, other: "SupplicantState") -> int:
|
|
if self._state == other._state:
|
|
return 0
|
|
return -1 if (self._VALID_STATES.index(self._state)
|
|
< self._VALID_STATES.index(other._state)) else 1
|
|
|
|
def equals(self, other) -> bool:
|
|
if isinstance(other, SupplicantState):
|
|
return self._state == other._state
|
|
return False
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._state) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiSsid
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiSsid(Stub):
|
|
"""WifiSsid — WiFi SSID 封装"""
|
|
|
|
def __init__(self, ssid: str = ""):
|
|
super().__init__()
|
|
self._ssid = ssid
|
|
|
|
@staticmethod
|
|
def createFromAsciiBytes(ssid_bytes: bytes) -> "WifiSsid":
|
|
try:
|
|
return WifiSsid(ssid_bytes.decode("utf-8"))
|
|
except UnicodeDecodeError:
|
|
return WifiSsid(repr(ssid_bytes))
|
|
|
|
@staticmethod
|
|
def createFromHex(hex_str: str) -> "WifiSsid":
|
|
try:
|
|
decoded = bytes.fromhex(hex_str).decode("utf-8", errors="replace")
|
|
return WifiSsid(decoded)
|
|
except Exception:
|
|
return WifiSsid(hex_str)
|
|
|
|
def toString(self) -> str:
|
|
return self._ssid
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiConfiguration
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiConfiguration(Stub):
|
|
"""WifiConfiguration — WiFi 网络配置"""
|
|
|
|
SECURITY_OPEN = 0
|
|
SECURITY_WEP = 1
|
|
SECURITY_PSK = 2
|
|
SECURITY_EAP = 3
|
|
SECURITY_SAE = 4
|
|
SECURITY_OWE = 5
|
|
|
|
class KeyMgmt(Stub):
|
|
NONE = 0
|
|
WPA_PSK = 1
|
|
WPA_EAP = 2
|
|
IEEE8021X = 3
|
|
WPA2_PSK = 4
|
|
OSEN = 5
|
|
FT_PSK = 6
|
|
FT_EAP = 7
|
|
SAE = 8
|
|
OWE = 9
|
|
SUITE_B_192 = 10
|
|
WPA3_SAE = 8
|
|
WPA3_OWE = 9
|
|
|
|
class GroupCipher(Stub):
|
|
NONE = 0
|
|
TKIP = 1
|
|
CCMP = 2
|
|
TKIP_CCMP = 3
|
|
AES = 4
|
|
SMS4 = 5
|
|
GCMP_128 = 6
|
|
GCMP_256 = 7
|
|
BIP_GMAC_128 = 8
|
|
BIP_GMAC_256 = 9
|
|
BIP_CMAC_256 = 10
|
|
|
|
class PairwiseCipher(Stub):
|
|
NONE = 0
|
|
TKIP = 1
|
|
CCMP = 2
|
|
NONE_ = 3
|
|
GCMP_128 = 4
|
|
GCMP_256 = 5
|
|
SMS4 = 6
|
|
|
|
class AuthAlgorithm(Stub):
|
|
OPEN = 0
|
|
SHARED = 1
|
|
LEAP = 2
|
|
|
|
class Protocol(Stub):
|
|
RSN = 0
|
|
WPA = 1
|
|
OSEN = 2
|
|
|
|
class Status(Stub):
|
|
CURRENT = 0
|
|
DISABLED = 1
|
|
ENABLED = 2
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.SSID: Optional[str] = None
|
|
self.BSSID: Optional[str] = None
|
|
self.preSharedKey: Optional[str] = None
|
|
self.status: int = WifiConfiguration.Status.DISABLED
|
|
self.networkId: int = -1
|
|
self.allowedKeyManagement: Set[int] = set()
|
|
self.allowedGroupCiphers: Set[int] = set()
|
|
self.allowedPairwiseCiphers: Set[int] = set()
|
|
self.allowedAuthAlgorithms: Set[int] = set()
|
|
self.allowedProtocols: Set[int] = set()
|
|
self.hiddenSSID: bool = False
|
|
self.priority: int = 0
|
|
self.wepKeys: List[Optional[str]] = [None, None, None, None]
|
|
self.wepTxKeyIndex: int = 0
|
|
self.isEnterprise: bool = False
|
|
self._enterprise_config: Optional["WifiEnterpriseConfig"] = None
|
|
|
|
def getNetworkId(self) -> int:
|
|
return self.networkId
|
|
|
|
def toString(self) -> str:
|
|
ssid = self.SSID or "(none)"
|
|
return f"WifiConfiguration(SSID={ssid}, id={self.networkId})"
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self.SSID) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiEnterpriseConfig
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiEnterpriseConfig(Stub):
|
|
"""WifiEnterpriseConfig — 企业级 WiFi 配置"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._identity: str = ""
|
|
self._password: str = ""
|
|
self._ca_cert: Optional[str] = None
|
|
self._client_cert: Optional[str] = None
|
|
self._subject_match: str = ""
|
|
self._alt_subject_match: str = ""
|
|
self._domain_suffix_match: str = ""
|
|
self._eap_method: int = -1
|
|
self._phase2_method: int = -1
|
|
|
|
def setIdentity(self, identity: str):
|
|
self._identity = identity
|
|
|
|
def getIdentity(self) -> str:
|
|
return self._identity
|
|
|
|
def setPassword(self, password: str):
|
|
self._password = password
|
|
|
|
def getPassword(self) -> str:
|
|
return self._password
|
|
|
|
def setCaCertificate(self, cert: Any):
|
|
pass
|
|
|
|
def getCaCertificate(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def setClientCertificate(self, cert: Any):
|
|
pass
|
|
|
|
def getClientCertificate(self) -> Optional[Any]:
|
|
return None
|
|
|
|
def setSubjectMatch(self, match: str):
|
|
self._subject_match = match
|
|
|
|
def getSubjectMatch(self) -> str:
|
|
return self._subject_match
|
|
|
|
def setDomainSuffixMatch(self, match: str):
|
|
self._domain_suffix_match = match
|
|
|
|
def getDomainSuffixMatch(self) -> str:
|
|
return self._domain_suffix_match
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiEnterpriseConfig(identity={self._identity})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiInfo
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiInfo(Stub):
|
|
"""WifiInfo — WiFi 连接信息"""
|
|
|
|
LINK_SPEED_UNITS = "Mbps"
|
|
STANDARD_LEGACY = 1
|
|
STANDARD_11N = 4
|
|
STANDARD_11AC = 5
|
|
STANDARD_11AX = 6
|
|
STANDARD_11BE = 7
|
|
SECURITY_TYPE_OPEN = 0
|
|
SECURITY_TYPE_WEP = 1
|
|
SECURITY_TYPE_PSK = 2
|
|
SECURITY_TYPE_EAP = 3
|
|
SECURITY_TYPE_SAE = 4
|
|
SECURITY_TYPE_OWE = 5
|
|
SECURITY_TYPE_WAPI_PSK = 6
|
|
SECURITY_TYPE_PASSPOINT_R1_R2 = 7
|
|
SECURITY_TYPE_PASSPOINT_R3 = 8
|
|
|
|
INVALID_RSSI = -127
|
|
DEFAULT_RSSI = -60
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._ssid: str = "<unknown ssid>"
|
|
self._bssid: str = "00:00:00:00:00:00"
|
|
self._rssi: int = self.INVALID_RSSI
|
|
self._link_speed: int = 0
|
|
self._frequency: int = 0
|
|
self._ip_address: int = 0
|
|
self._mac_address: str = "02:00:00:00:00:00"
|
|
self._network_id: int = -1
|
|
self._supplicant_state: SupplicantState = SupplicantState(SupplicantState.DISCONNECTED)
|
|
self._wifi_standard: int = self.STANDARD_LEGACY
|
|
self._is_hidden_ssid: bool = False
|
|
self._tx_link_speed: int = 0
|
|
self._rx_link_speed: int = 0
|
|
self._max_rx_link_speed: int = 0
|
|
self._max_tx_link_speed: int = 0
|
|
self._security_type: int = self.SECURITY_TYPE_OPEN
|
|
self._rssi_timestamp: int = 0
|
|
|
|
def getSSID(self) -> str:
|
|
return self._ssid
|
|
|
|
def getBSSID(self) -> str:
|
|
return self._bssid
|
|
|
|
def getRssi(self) -> int:
|
|
return self._rssi
|
|
|
|
def getLinkSpeed(self) -> int:
|
|
return self._link_speed
|
|
|
|
def getFrequency(self) -> int:
|
|
return self._frequency
|
|
|
|
def getIpAddress(self) -> int:
|
|
return self._ip_address
|
|
|
|
def getMacAddress(self) -> str:
|
|
return self._mac_address
|
|
|
|
def getNetworkId(self) -> int:
|
|
return self._network_id
|
|
|
|
def getSupplicantState(self) -> SupplicantState:
|
|
return self._supplicant_state
|
|
|
|
def getWifiStandard(self) -> int:
|
|
return self._wifi_standard
|
|
|
|
def isHiddenSSID(self) -> bool:
|
|
return self._is_hidden_ssid
|
|
|
|
def getTxLinkSpeedMbps(self) -> int:
|
|
return self._tx_link_speed
|
|
|
|
def getRxLinkSpeedMbps(self) -> int:
|
|
return self._rx_link_speed
|
|
|
|
def getMaxSupportedRxLinkSpeedMbps(self) -> int:
|
|
return self._max_rx_link_speed
|
|
|
|
def getMaxSupportedTxLinkSpeedMbps(self) -> int:
|
|
return self._max_tx_link_speed
|
|
|
|
def getSecurityType(self) -> int:
|
|
return self._security_type
|
|
|
|
def getRssiTimestamp(self) -> int:
|
|
return self._rssi_timestamp
|
|
|
|
def toString(self) -> str:
|
|
return (f"WifiInfo(SSID={self._ssid}, BSSID={self._bssid}, "
|
|
f"rssi={self._rssi}, speed={self._link_speed} {self.LINK_SPEED_UNITS})")
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._bssid) & 0x7FFFFFFF
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# ScanResult
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class ScanResult(Stub):
|
|
"""ScanResult — WiFi 扫描结果"""
|
|
|
|
WIFI_BAND_24_GHZ = 0
|
|
WIFI_BAND_5_GHZ = 1
|
|
WIFI_BAND_6_GHZ = 2
|
|
CHANNEL_WIDTH_20MHZ = 0
|
|
CHANNEL_WIDTH_40MHZ = 1
|
|
CHANNEL_WIDTH_80MHZ = 2
|
|
CHANNEL_WIDTH_160MHZ = 3
|
|
CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4
|
|
|
|
UNSPECIFIED = -1
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.SSID: str = ""
|
|
self.BSSID: str = ""
|
|
self.capabilities: str = ""
|
|
self.level: int = 0
|
|
self.frequency: int = 0
|
|
self.timestamp: int = 0
|
|
self.channelWidth: int = self.CHANNEL_WIDTH_20MHZ
|
|
self.centerFreq0: int = 0
|
|
self.centerFreq1: int = 0
|
|
self.operatorFriendlyName: str = ""
|
|
self.venueName: str = ""
|
|
self.is80211mcResponder: bool = False
|
|
self.isPasspointNetwork: bool = False
|
|
self._wifi_standard: int = 0
|
|
self._seen: int = 0
|
|
self._distance: Optional[float] = None
|
|
self._distance_sd: Optional[float] = None
|
|
|
|
def getSSID(self) -> str:
|
|
return self.SSID
|
|
|
|
def getBSSID(self) -> str:
|
|
return self.BSSID
|
|
|
|
def getCapabilities(self) -> str:
|
|
return self.capabilities
|
|
|
|
def getLevel(self) -> int:
|
|
return self.level
|
|
|
|
def getFrequency(self) -> int:
|
|
return self.frequency
|
|
|
|
def getTimestamp(self) -> int:
|
|
return self.timestamp
|
|
|
|
def getChannelWidth(self) -> int:
|
|
return self.channelWidth
|
|
|
|
def getCenterFreq0(self) -> int:
|
|
return self.centerFreq0
|
|
|
|
def getCenterFreq1(self) -> int:
|
|
return self.centerFreq1
|
|
|
|
def getOperatorFriendlyName(self) -> str:
|
|
return self.operatorFriendlyName
|
|
|
|
def getVenueName(self) -> str:
|
|
return self.venueName
|
|
|
|
def is80211mcResponder(self) -> bool:
|
|
return self.is80211mcResponder
|
|
|
|
def isPasspointNetwork(self) -> bool:
|
|
return self.isPasspointNetwork
|
|
|
|
def getWifiStandard(self) -> int:
|
|
return self._wifi_standard
|
|
|
|
def getDistance(self) -> Optional[float]:
|
|
return self._distance
|
|
|
|
def getDistanceSd(self) -> Optional[float]:
|
|
return self._distance_sd
|
|
|
|
def toString(self) -> str:
|
|
return f"ScanResult(SSID={self.SSID}, BSSID={self.BSSID}, level={self.level})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiNetworkSpecifier
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiNetworkSpecifier(Stub):
|
|
"""WifiNetworkSpecifier — WiFi 网络说明符"""
|
|
|
|
def __init__(self, ssid: Optional[str] = None, bssid: Optional[str] = None,
|
|
band: int = 0, security_type: int = 0):
|
|
super().__init__()
|
|
self._ssid = ssid
|
|
self._bssid = bssid
|
|
self._band = band
|
|
self._security_type = security_type
|
|
|
|
def getSsid(self) -> Optional[str]:
|
|
return self._ssid
|
|
|
|
def getBssid(self) -> Optional[str]:
|
|
return self._bssid
|
|
|
|
def getBand(self) -> int:
|
|
return self._band
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiNetworkSpecifier(SSID={self._ssid})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiNetworkSuggestion
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiNetworkSuggestion(Stub):
|
|
"""WifiNetworkSuggestion — WiFi 网络建议"""
|
|
|
|
def __init__(self, ssid: str = "", bssid: Optional[str] = None,
|
|
is_hidden: bool = False, priority: int = 0,
|
|
wpa2_passphrase: Optional[str] = None,
|
|
wpa3_passphrase: Optional[str] = None,
|
|
is_owe: bool = False):
|
|
super().__init__()
|
|
self._ssid = ssid
|
|
self._bssid = bssid
|
|
self._is_hidden = is_hidden
|
|
self._priority = priority
|
|
self._wpa2_passphrase = wpa2_passphrase
|
|
self._wpa3_passphrase = wpa3_passphrase
|
|
self._is_owe = is_owe
|
|
|
|
def getSsid(self) -> str:
|
|
return self._ssid
|
|
|
|
def getBssid(self) -> Optional[str]:
|
|
return self._bssid
|
|
|
|
def isHidden(self) -> bool:
|
|
return self._is_hidden
|
|
|
|
def getPriority(self) -> int:
|
|
return self._priority
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiNetworkSuggestion(SSID={self._ssid})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiManager helpers
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiManagerMulticastLock(Stub):
|
|
"""WifiManager.MulticastLock"""
|
|
|
|
def __init__(self, tag: str):
|
|
super().__init__()
|
|
self._tag = tag
|
|
self._held = False
|
|
self._ref_count = 0
|
|
|
|
def acquire(self):
|
|
self._held = True
|
|
self._ref_count += 1
|
|
|
|
def release(self):
|
|
if self._ref_count > 0:
|
|
self._ref_count -= 1
|
|
if self._ref_count == 0:
|
|
self._held = False
|
|
|
|
def isHeld(self) -> bool:
|
|
return self._held
|
|
|
|
def toString(self) -> str:
|
|
return f"MulticastLock(tag={self._tag}, held={self._held})"
|
|
|
|
|
|
class WifiManagerLocalOnlyHotspotCallback(Stub):
|
|
"""WifiManager.LocalOnlyHotspotCallback"""
|
|
|
|
ERROR_NO_CHANNEL = 1
|
|
ERROR_GENERIC = 2
|
|
ERROR_INCOMPATIBLE_MODE = 3
|
|
ERROR_TETHERING_DISALLOWED = 4
|
|
|
|
def onStarted(self, hotspot: Any):
|
|
pass
|
|
|
|
def onStopped(self):
|
|
pass
|
|
|
|
def onFailed(self, reason: int):
|
|
pass
|
|
|
|
|
|
class WifiManagerWpsCallback(Stub):
|
|
"""WifiManager.WpsCallback"""
|
|
|
|
def onStarted(self, pin: str):
|
|
pass
|
|
|
|
def onSucceeded(self):
|
|
pass
|
|
|
|
def onFailed(self, reason: int):
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiManager(Stub):
|
|
"""WifiManager — WiFi 管理器"""
|
|
|
|
WIFI_STATE_DISABLING = 0
|
|
WIFI_STATE_DISABLED = 1
|
|
WIFI_STATE_ENABLING = 2
|
|
WIFI_STATE_ENABLED = 3
|
|
WIFI_STATE_UNKNOWN = 4
|
|
|
|
WIFI_MODE_FULL = 1
|
|
WIFI_MODE_FULL_HIGH_PERF = 3
|
|
WIFI_MODE_SCAN_ONLY = 2
|
|
|
|
ERROR_AUTHENTICATING = 1
|
|
SAP_START_FAILURE_GENERAL = 0
|
|
|
|
ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"
|
|
SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE"
|
|
SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.SUPPLICANT_STATE_CHANGED"
|
|
NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE"
|
|
SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS"
|
|
WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED"
|
|
RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED"
|
|
NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED"
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._wifi_state = self.WIFI_STATE_ENABLED
|
|
self._wifi_enabled = True
|
|
self._connection_info: Optional[WifiInfo] = None
|
|
self._scan_results: List[ScanResult] = []
|
|
self._configured_networks: List[WifiConfiguration] = []
|
|
self._network_suggestions: List[WifiNetworkSuggestion] = []
|
|
self._listeners: Dict[str, List[Any]] = {}
|
|
self._next_network_id = 0
|
|
self._p2p_channels: List[int] = []
|
|
|
|
def getConnectionInfo(self) -> Optional[WifiInfo]:
|
|
if _USE_LINUX:
|
|
return self._get_linux_connection_info()
|
|
if self._connection_info is None:
|
|
info = WifiInfo()
|
|
info._ssid = '"mock_wifi"'
|
|
info._bssid = "00:1A:2B:3C:4D:5E"
|
|
info._rssi = -55
|
|
info._link_speed = 1300
|
|
info._frequency = 5180
|
|
info._network_id = 0
|
|
info._supplicant_state = SupplicantState(SupplicantState.COMPLETED)
|
|
self._connection_info = info
|
|
return self._connection_info
|
|
|
|
def getWifiInfo(self) -> Optional[WifiInfo]:
|
|
"""getWifiInfo — getConnectionInfo 的别名"""
|
|
return self.getConnectionInfo()
|
|
|
|
def _get_linux_connection_info(self) -> WifiInfo:
|
|
"""从 LinuxNetwork.get_wifi_info() 构造 WifiInfo"""
|
|
info = WifiInfo()
|
|
try:
|
|
data = LinuxNetwork.get_wifi_info() or {}
|
|
ssid = data.get("ssid", "")
|
|
info._ssid = f'"{ssid}"' if ssid else "<unknown ssid>"
|
|
info._bssid = data.get("bssid", "00:00:00:00:00:00")
|
|
# signal 通常是 0-100, 转换为 RSSI (dBm, 约 -100..0)
|
|
signal = data.get("signal", 0)
|
|
if isinstance(signal, (int, float)) and signal > 0:
|
|
info._rssi = int(-100 + (signal / 100.0) * 60)
|
|
else:
|
|
info._rssi = int(signal) if signal else WifiInfo.INVALID_RSSI
|
|
iface = data.get("interface", "")
|
|
if iface:
|
|
info._frequency = 2412 # 默认 2.4GHz
|
|
info._link_speed = data.get("link_speed", 0) or 0
|
|
info._network_id = 0
|
|
info._supplicant_state = SupplicantState(SupplicantState.COMPLETED)
|
|
except Exception:
|
|
pass
|
|
return info
|
|
|
|
def getScanResults(self) -> List[ScanResult]:
|
|
return list(self._scan_results)
|
|
|
|
def startScan(self) -> bool:
|
|
return True
|
|
|
|
def setWifiEnabled(self, enabled: bool) -> bool:
|
|
self._wifi_enabled = enabled
|
|
self._wifi_state = self.WIFI_STATE_ENABLED if enabled else self.WIFI_STATE_DISABLED
|
|
return True
|
|
|
|
def isWifiEnabled(self) -> bool:
|
|
if _USE_LINUX:
|
|
return self._linux_has_wireless_interface()
|
|
return self._wifi_enabled
|
|
|
|
def _linux_has_wireless_interface(self) -> bool:
|
|
"""检查是否存在无线网络接口"""
|
|
try:
|
|
for iface in LinuxNetwork.get_interfaces():
|
|
name = iface.get("ifname") or iface.get("name", "")
|
|
if name and name.startswith(("wl", "wlan", "wlp")):
|
|
return True
|
|
# 回退: 检查 /sys/class/net 下的无线接口
|
|
import os as _os
|
|
for name in _os.listdir("/sys/class/net/") if _os.path.isdir("/sys/class/net") else []:
|
|
if name.startswith(("wl", "wlan", "wlp")):
|
|
return True
|
|
except Exception:
|
|
pass
|
|
return False
|
|
|
|
def getWifiState(self) -> int:
|
|
return self._wifi_state
|
|
|
|
def getConfiguredNetworks(self) -> List[WifiConfiguration]:
|
|
return list(self._configured_networks)
|
|
|
|
def addNetwork(self, config: WifiConfiguration) -> int:
|
|
config.networkId = self._next_network_id
|
|
self._next_network_id += 1
|
|
self._configured_networks.append(config)
|
|
return config.networkId
|
|
|
|
def updateNetwork(self, config: WifiConfiguration) -> int:
|
|
for i, c in enumerate(self._configured_networks):
|
|
if c.networkId == config.networkId:
|
|
self._configured_networks[i] = config
|
|
return config.networkId
|
|
return -1
|
|
|
|
def removeNetwork(self, net_id: int) -> bool:
|
|
self._configured_networks = [
|
|
c for c in self._configured_networks if c.networkId != net_id
|
|
]
|
|
return True
|
|
|
|
def enableNetwork(self, net_id: int, disable_others: bool = False) -> bool:
|
|
for c in self._configured_networks:
|
|
if c.networkId == net_id:
|
|
c.status = WifiConfiguration.Status.ENABLED
|
|
elif disable_others:
|
|
c.status = WifiConfiguration.Status.DISABLED
|
|
return True
|
|
|
|
def disableNetwork(self, net_id: int) -> bool:
|
|
for c in self._configured_networks:
|
|
if c.networkId == net_id:
|
|
c.status = WifiConfiguration.Status.DISABLED
|
|
return True
|
|
|
|
def disconnect(self) -> bool:
|
|
return True
|
|
|
|
def reconnect(self) -> bool:
|
|
return True
|
|
|
|
def reassociate(self) -> bool:
|
|
return True
|
|
|
|
def is5GHzBandSupported(self) -> bool:
|
|
return True
|
|
|
|
def is6GHzBandSupported(self) -> bool:
|
|
import sys
|
|
return sys.version_info >= (3, 10)
|
|
|
|
def isEasyConnectSupported(self) -> bool:
|
|
return False
|
|
|
|
def getP2pChannelList(self) -> List[int]:
|
|
return list(self._p2p_channels)
|
|
|
|
def startLocalOnlyHotspot(self, callback: WifiManagerLocalOnlyHotspotCallback, handler: Any = None):
|
|
try:
|
|
callback.onStarted("MockHotspot")
|
|
except Exception:
|
|
pass
|
|
|
|
def addNetworkSuggestions(self, suggestions: List[WifiNetworkSuggestion]) -> int:
|
|
self._network_suggestions.extend(suggestions)
|
|
return 0 # STATUS_SUGGESTION_SUCCESS
|
|
|
|
def removeNetworkSuggestions(self, suggestions: List[WifiNetworkSuggestion]) -> int:
|
|
for s in suggestions:
|
|
self._network_suggestions = [x for x in self._network_suggestions if x is not s]
|
|
return 0
|
|
|
|
def createMulticastLock(self, tag: str) -> WifiManagerMulticastLock:
|
|
return WifiManagerMulticastLock(tag)
|
|
|
|
def getDhcpInfo(self):
|
|
from .net import DhcpInfo
|
|
return DhcpInfo()
|
|
|
|
def pingSupplicant(self) -> bool:
|
|
return True
|
|
|
|
def startScanActive(self) -> bool:
|
|
return True
|
|
|
|
def setScanAlwaysAvailable(self, enabled: bool):
|
|
pass
|
|
|
|
def isScanAlwaysAvailable(self) -> bool:
|
|
return True
|
|
|
|
def saveConfiguration(self) -> bool:
|
|
return True
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiManager(state={self._wifi_state})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiP2pManager
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
class WifiP2pDevice(Stub):
|
|
"""WifiP2pDevice — P2P 设备"""
|
|
|
|
CONNECTED = 0
|
|
INVITED = 1
|
|
FAILED = 2
|
|
AVAILABLE = 3
|
|
UNAVAILABLE = 4
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.deviceAddress: str = ""
|
|
self.deviceName: str = ""
|
|
self.primaryDeviceType: str = ""
|
|
self.secondaryDeviceType: str = ""
|
|
self.wpsConfigMethods: int = 0
|
|
self.deviceCapability: int = 0
|
|
self.groupCapability: int = 0
|
|
self.status: int = self.UNAVAILABLE
|
|
self.GO_PORT: int = 0
|
|
|
|
def getDeviceAddress(self) -> str:
|
|
return self.deviceAddress
|
|
|
|
def getDeviceName(self) -> str:
|
|
return self.deviceName
|
|
|
|
def isServiceDiscoveryCapable(self) -> bool:
|
|
return True
|
|
|
|
def isGroupOwner(self) -> bool:
|
|
return self.groupCapability > 0
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiP2pDevice(name={self.deviceName}, addr={self.deviceAddress})"
|
|
|
|
|
|
class WifiP2pConfig(Stub):
|
|
"""WifiP2pConfig — P2P 配置"""
|
|
|
|
class Builder(Stub):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._device_address = ""
|
|
self._network_name = ""
|
|
self._passphrase = ""
|
|
self._group_owner_band = 0
|
|
|
|
def setDeviceAddress(self, addr: str) -> "WifiP2pConfig.Builder":
|
|
self._device_address = addr
|
|
return self
|
|
|
|
def setNetworkName(self, name: str) -> "WifiP2pConfig.Builder":
|
|
self._network_name = name
|
|
return self
|
|
|
|
def setPassphrase(self, passphrase: str) -> "WifiP2pConfig.Builder":
|
|
self._passphrase = passphrase
|
|
return self
|
|
|
|
def setGroupOwnerBand(self, band: int) -> "WifiP2pConfig.Builder":
|
|
self._group_owner_band = band
|
|
return self
|
|
|
|
def build(self) -> "WifiP2pConfig":
|
|
config = WifiP2pConfig()
|
|
config.deviceAddress = self._device_address
|
|
config.networkName = self._network_name
|
|
config.passphrase = self._passphrase
|
|
return config
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.deviceAddress: str = ""
|
|
self.networkName: str = ""
|
|
self.passphrase: str = ""
|
|
|
|
|
|
class WifiP2pGroup(Stub):
|
|
"""WifiP2pGroup — P2P 群组"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._network_name: str = ""
|
|
self._is_group_owner: bool = False
|
|
self._owner: Optional[WifiP2pDevice] = None
|
|
self._clients: List[WifiP2pDevice] = []
|
|
self._passphrase: str = ""
|
|
self._interface: str = ""
|
|
|
|
def getNetworkName(self) -> str:
|
|
return self._network_name
|
|
|
|
def isGroupOwner(self) -> bool:
|
|
return self._is_group_owner
|
|
|
|
def getOwner(self) -> Optional[WifiP2pDevice]:
|
|
return self._owner
|
|
|
|
def getClientList(self) -> List[WifiP2pDevice]:
|
|
return list(self._clients)
|
|
|
|
def getPassphrase(self) -> str:
|
|
return self._passphrase
|
|
|
|
def getInterface(self) -> str:
|
|
return self._interface
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiP2pGroup(name={self._network_name})"
|
|
|
|
|
|
class WifiP2pManager(Stub):
|
|
"""WifiP2pManager — P2P 管理器"""
|
|
|
|
WIFI_P2P_STATE_ENABLED_ACTION = "android.net.wifi.p2p.STATE_CHANGED"
|
|
WIFI_P2P_STATE_CHANGED_ACTION = "android.net.wifi.p2p.STATE_CHANGED"
|
|
WIFI_P2P_PEERS_CHANGED_ACTION = "android.net.wifi.p2p.PEERS_CHANGED"
|
|
WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_CHANGED"
|
|
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION = "android.net.wifi.p2p.THIS_DEVICE_CHANGED"
|
|
WIFI_P2P_DISCOVERY_CHANGED_ACTION = "android.net.wifi.p2p.DISCOVERY_CHANGED"
|
|
|
|
class Channel(Stub):
|
|
"""P2P 通道"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._channel: Optional[WifiP2pManager.Channel] = None
|
|
self._peers: List[WifiP2pDevice] = []
|
|
|
|
def initialize(self, context: Any, looper: Any, listener: Any) -> "WifiP2pManager.Channel":
|
|
self._channel = WifiP2pManager.Channel()
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
return self._channel
|
|
|
|
def discoverPeers(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def discoverServices(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def requestPeers(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onPeersAvailable(self._peers)
|
|
except Exception:
|
|
pass
|
|
|
|
def requestGroupInfo(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onGroupInfoAvailable(WifiP2pGroup())
|
|
except Exception:
|
|
pass
|
|
|
|
def createGroup(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def removeGroup(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def connect(self, channel: "WifiP2pManager.Channel", config: WifiP2pConfig, listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def cancelConnect(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def stopPeerDiscovery(self, channel: "WifiP2pManager.Channel", listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def requestDeviceInfo(self, channel: "WifiP2pManager.Channel", device_address: str, listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onDeviceInfoAvailable(WifiP2pDevice())
|
|
except Exception:
|
|
pass
|
|
|
|
def setDeviceName(self, channel: "WifiP2pManager.Channel", device_name: str, listener: Any):
|
|
if listener:
|
|
try:
|
|
listener.onSuccess()
|
|
except Exception:
|
|
pass
|
|
|
|
def setWfdEnabled(self, channel: "WifiP2pManager.Channel", enabled: bool, listener: Any):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"WifiP2pManager(peers={len(self._peers)})"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# WifiConfigurationKeyMgmt / WifiConfiguration... aliases
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
WifiConfigurationKeyMgmt = WifiConfiguration.KeyMgmt
|
|
WifiConfigurationGroupCipher = WifiConfiguration.GroupCipher
|
|
WifiConfigurationPairwiseCipher = WifiConfiguration.PairwiseCipher
|
|
WifiConfigurationAuthAlgorithm = WifiConfiguration.AuthAlgorithm
|
|
WifiConfigurationProtocol = WifiConfiguration.Protocol
|
|
WifiConfigurationStatus = WifiConfiguration.Status
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════
|
|
# __all__
|
|
# ═══════════════════════════════════════════════════════════════
|
|
|
|
__all__ = [
|
|
"WifiManager",
|
|
"WifiInfo",
|
|
"WifiConfiguration",
|
|
"WifiConfigurationKeyMgmt",
|
|
"WifiConfigurationGroupCipher",
|
|
"WifiConfigurationPairwiseCipher",
|
|
"WifiConfigurationAuthAlgorithm",
|
|
"WifiConfigurationProtocol",
|
|
"WifiConfigurationStatus",
|
|
"ScanResult",
|
|
"WifiSsid",
|
|
"SupplicantState",
|
|
"WifiEnterpriseConfig",
|
|
"WifiNetworkSpecifier",
|
|
"WifiNetworkSuggestion",
|
|
"WifiManagerLocalOnlyHotspotCallback",
|
|
"WifiManagerWpsCallback",
|
|
"WifiManagerMulticastLock",
|
|
"WifiP2pManager",
|
|
"WifiP2pDevice",
|
|
"WifiP2pConfig",
|
|
"WifiP2pGroup",
|
|
] |