""" Android Companion Device API 扩展 — CompanionDeviceManager (扩展), AssociationRequest, DeviceFilter, DevicePresenceEvent, CompanionTransport, CompanionWireless, CompanionManager 等 方法签名完全对齐 Android SDK """ from __future__ import annotations from typing import Any, Callable, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) __all__ = [ "CompanionDeviceManager", "AssociationRequest", "DeviceFilter", "DevicePresenceEvent", "DevicePresenceNotifier", "CompanionTransport", "CompanionWireless", "CompanionBluetooth", "CompanionWifi", "CompanionEthernet", "CompanionVendor", "CompanionManager", ] # ═══════════════════════════════════════════════════════════════ # 常量 # ═══════════════════════════════════════════════════════════════ DEVICE_PRESENCE_ENTER = 0 DEVICE_PRESENCE_EXIT = 1 DEVICE_PRESENCE_PROXIMITY_NEAR = 2 DEVICE_PRESENCE_PROXIMITY_FAR = 3 DEVICE_PRESENCE_APPEAR = 4 DEVICE_PRESENCE_DISAPPEAR = 5 DEVICE_PRESENCE_AVAILABLE = 6 DEVICE_PRESENCE_UNAVAILABLE = 7 DEVICE_FLAG_NONE = 0 DEVICE_FLAG_SELF_MANAGED = 1 DEVICE_FLAG_NOT_ASSOCIATED = 2 DEVICE_FLAG_SYSTEM_SERVICE = 4 DEVICE_FLAG_DEBUG_ONLY = 8 DEVICE_FLAG_VENDOR_SPECIFIC = 16 COMPANION_TRANSPORT_BLUETOOTH = 0 COMPANION_TRANSPORT_BLUETOOTH_LE = 1 COMPANION_TRANSPORT_WIFI = 2 COMPANION_TRANSPORT_ETHERNET = 3 COMPANION_TRANSPORT_USB = 4 COMPANION_TRANSPORT_NFC = 5 COMPANION_TRANSPORT_VENDOR = 100 ASSOCIATION_PENDING = 0 ASSOCIATION_ESTABLISHED = 1 ASSOCIATION_FAILED = 2 ASSOCIATION_CANCELLED = 3 ASSOCIATION_REVOKED = 4 # ═══════════════════════════════════════════════════════════════ # DeviceFilter # ═══════════════════════════════════════════════════════════════ class DeviceFilter(Stub): """DeviceFilter — 设备过滤器基类""" def __init__(self, namePattern: str = "", address: str = "", isSelfManaged: bool = False): super().__init__() self._name_pattern = namePattern self._address = address self._is_self_managed = isSelfManaged def getNamePattern(self) -> str: return self._name_pattern def getAddress(self) -> str: return self._address def isSelfManaged(self) -> bool: return self._is_self_managed def matches(self, device: Any) -> bool: return True def toString(self) -> str: return f"DeviceFilter(pattern={self._name_pattern})" def equals(self, other: Any) -> bool: if not isinstance(other, DeviceFilter): return False return self._address == other._address def hashCode(self) -> int: return hash(self._address) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # AssociationRequest # ═══════════════════════════════════════════════════════════════ class AssociationRequest(Stub): """AssociationRequest — 关联请求""" def __init__(self, isSelfManaged: bool = False, deviceFilter: Optional[DeviceFilter] = None, displayName: str = "", transportTypes: List[int] = None, flags: int = DEVICE_FLAG_NONE, profile: str = ""): super().__init__() self._is_self_managed = isSelfManaged self._device_filter = deviceFilter self._display_name = displayName self._transport_types = transportTypes or [] self._flags = flags self._profile = profile def isSelfManaged(self) -> bool: return self._is_self_managed def getDeviceFilter(self) -> Optional[DeviceFilter]: return self._device_filter def getDisplayName(self) -> str: return self._display_name def getTransportTypes(self) -> List[int]: return list(self._transport_types) def getFlags(self) -> int: return self._flags def getProfile(self) -> str: return self._profile class Builder(Stub): """AssociationRequest.Builder — 关联请求构建器""" def __init__(self): super().__init__() self._is_self_managed = False self._device_filter: Optional[DeviceFilter] = None self._display_name = "" self._transport_types: List[int] = [] self._flags = DEVICE_FLAG_NONE self._profile = "" def setSelfManaged(self, selfManaged: bool) -> "AssociationRequest.Builder": self._is_self_managed = selfManaged return self def setDeviceFilter(self, filter_: DeviceFilter) -> "AssociationRequest.Builder": self._device_filter = filter_ return self def setDisplayName(self, name: str) -> "AssociationRequest.Builder": self._display_name = name return self def addTransportType(self, transportType: int) -> "AssociationRequest.Builder": if transportType not in self._transport_types: self._transport_types.append(transportType) return self def setFlags(self, flags: int) -> "AssociationRequest.Builder": self._flags = flags return self def setProfile(self, profile: str) -> "AssociationRequest.Builder": self._profile = profile return self def build(self) -> "AssociationRequest": return AssociationRequest( isSelfManaged=self._is_self_managed, deviceFilter=self._device_filter, displayName=self._display_name, transportTypes=self._transport_types, flags=self._flags, profile=self._profile, ) def toString(self) -> str: return (f"AssociationRequest(selfManaged={self._is_self_managed}, " f"name={self._display_name})") def equals(self, other: Any) -> bool: if not isinstance(other, AssociationRequest): return False return (self._is_self_managed == other._is_self_managed and self._display_name == other._display_name) def hashCode(self) -> int: return hash((self._is_self_managed, self._display_name)) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # DevicePresenceEvent # ═══════════════════════════════════════════════════════════════ class DevicePresenceEvent(Stub): """DevicePresenceEvent — 设备存在事件""" def __init__(self, deviceAddress: str = "", eventType: int = DEVICE_PRESENCE_ENTER, timestamp: int = 0, rssi: int = 0, transportType: int = COMPANION_TRANSPORT_BLUETOOTH, deviceId: str = ""): super().__init__() self._device_address = deviceAddress self._event_type = eventType self._timestamp = timestamp self._rssi = rssi self._transport_type = transportType self._device_id = deviceId def getDeviceAddress(self) -> str: return self._device_address def getEventType(self) -> int: return self._event_type def getTimestamp(self) -> int: return self._timestamp def getRssi(self) -> int: return self._rssi def getTransportType(self) -> int: return self._transport_type def getDeviceId(self) -> str: return self._device_id def toString(self) -> str: return (f"DevicePresenceEvent(addr={self._device_address}, " f"type={self._event_type})") def equals(self, other: Any) -> bool: if not isinstance(other, DevicePresenceEvent): return False return (self._device_address == other._device_address and self._event_type == other._event_type) def hashCode(self) -> int: return hash((self._device_address, self._event_type)) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # DevicePresenceNotifier # ═══════════════════════════════════════════════════════════════ class DevicePresenceNotifier(Stub): """DevicePresenceNotifier — 设备存在通知器""" def __init__(self): super().__init__() self._listeners: Dict[int, Callable] = {} def registerCallback(self, callback: Callable, handler: Any = None): lid = id(callback) self._listeners[lid] = callback def unregisterCallback(self, callback: Callable): lid = id(callback) self._listeners.pop(lid, None) def notifyPresence(self, event: DevicePresenceEvent): for cb in self._listeners.values(): try: cb(event) except Exception: pass def toString(self) -> str: return f"DevicePresenceNotifier(listeners={len(self._listeners)})" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # CompanionTransport # ═══════════════════════════════════════════════════════════════ class CompanionTransport(Stub): """CompanionTransport — 配套设备传输基类""" def __init__(self, transportType: int = COMPANION_TRANSPORT_BLUETOOTH, name: str = ""): super().__init__() self._transport_type = transportType self._name = name def getTransportType(self) -> int: return self._transport_type def getName(self) -> str: return self._name def isAvailable(self) -> bool: return True def isEnabled(self) -> bool: return True def enable(self): pass def disable(self): pass def toString(self) -> str: return f"CompanionTransport(type={self._transport_type})" # ═══════════════════════════════════════════════════════════════ # CompanionWireless (WiFi 传输) # ═══════════════════════════════════════════════════════════════ class CompanionWireless(CompanionTransport): """CompanionWireless — 无线传输 (WiFi Direct / WiFi Aware)""" SECURITY_NONE = 0 SECURITY_WPA2 = 1 SECURITY_WPA3 = 2 SECURITY_WPA2_WPA3 = 3 def __init__(self, ssid: str = "", bssid: str = "", frequency: int = 0, securityType: int = SECURITY_WPA2): super().__init__(transportType=COMPANION_TRANSPORT_WIFI, name="Wifi") self._ssid = ssid self._bssid = bssid self._frequency = frequency self._security_type = securityType def getSsid(self) -> str: return self._ssid def getBssid(self) -> str: return self._bssid def getFrequency(self) -> int: return self._frequency def getSecurityType(self) -> int: return self._security_type def toString(self) -> str: return f"CompanionWireless(ssid={self._ssid})" # ═══════════════════════════════════════════════════════════════ # CompanionBluetooth # ═══════════════════════════════════════════════════════════════ class CompanionBluetooth(CompanionTransport): """CompanionBluetooth — 蓝牙传输""" TRANSPORT_BREDR = 0 TRANSPORT_LE = 1 TRANSPORT_DUAL = 2 def __init__(self, address: str = "", name: str = "", transport: int = TRANSPORT_BREDR, isBonded: bool = False): super().__init__(transportType=COMPANION_TRANSPORT_BLUETOOTH, name="Bluetooth") self._address = address self._device_name = name self._transport = transport self._is_bonded = isBonded def getAddress(self) -> str: return self._address def getDeviceName(self) -> str: return self._device_name def getTransportMode(self) -> int: return self._transport def isBonded(self) -> bool: return self._is_bonded def toString(self) -> str: return f"CompanionBluetooth(addr={self._address}, name={self._device_name})" # ═══════════════════════════════════════════════════════════════ # CompanionWifi # ═══════════════════════════════════════════════════════════════ class CompanionWifi(CompanionTransport): """CompanionWifi — WiFi 传输""" def __init__(self, ssid: str = "", bssid: str = "", ipAddress: str = "", port: int = 0, isDirect: bool = False, isAware: bool = False): super().__init__(transportType=COMPANION_TRANSPORT_WIFI, name="WiFi") self._ssid = ssid self._bssid = bssid self._ip_address = ipAddress self._port = port self._is_direct = isDirect self._is_aware = isAware def getSsid(self) -> str: return self._ssid def getBssid(self) -> str: return self._bssid def getIpAddress(self) -> str: return self._ip_address def getPort(self) -> int: return self._port def isDirect(self) -> bool: return self._is_direct def isAware(self) -> bool: return self._is_aware def toString(self) -> str: return f"CompanionWifi(ssid={self._ssid})" # ═══════════════════════════════════════════════════════════════ # CompanionEthernet # ═══════════════════════════════════════════════════════════════ class CompanionEthernet(CompanionTransport): """CompanionEthernet — 以太网传输""" def __init__(self, interfaceName: str = "", macAddress: str = "", ipAddress: str = "", linkSpeed: int = 1000): super().__init__(transportType=COMPANION_TRANSPORT_ETHERNET, name="Ethernet") self._interface_name = interfaceName self._mac_address = macAddress self._ip_address = ipAddress self._link_speed = linkSpeed def getInterfaceName(self) -> str: return self._interface_name def getMacAddress(self) -> str: return self._mac_address def getIpAddress(self) -> str: return self._ip_address def getLinkSpeed(self) -> int: return self._link_speed def toString(self) -> str: return f"CompanionEthernet(iface={self._interface_name})" # ═══════════════════════════════════════════════════════════════ # CompanionVendor # ═══════════════════════════════════════════════════════════════ class CompanionVendor(CompanionTransport): """CompanionVendor — 厂商自定义传输""" class RfcommEndpoint(Stub): """CompanionVendor.RfcommEndpoint — RFCOMM 端点""" def __init__(self, address: str = "", channel: int = 0, uuid: str = "", name: str = ""): super().__init__() self._address = address self._channel = channel self._uuid = uuid self._name = name def getAddress(self) -> str: return self._address def getChannel(self) -> int: return self._channel def getUuid(self) -> str: return self._uuid def getName(self) -> str: return self._name def toString(self) -> str: return f"RfcommEndpoint(addr={self._address}, channel={self._channel})" def equals(self, other: Any) -> bool: if not isinstance(other, CompanionVendor.RfcommEndpoint): return False return self._address == other._address and self._channel == other._channel def hashCode(self) -> int: return hash((self._address, self._channel)) & 0x7FFFFFFF class Transport(Stub): """CompanionVendor.Transport — 厂商传输层""" def __init__(self, name: str = "", vendorId: str = "", version: int = 1, endpoints: List["CompanionVendor.RfcommEndpoint"] = None): super().__init__() self._name = name self._vendor_id = vendorId self._version = version self._endpoints = endpoints or [] def getName(self) -> str: return self._name def getVendorId(self) -> str: return self._vendor_id def getVersion(self) -> int: return self._version def getEndpoints(self) -> List["CompanionVendor.RfcommEndpoint"]: return list(self._endpoints) def addEndpoint(self, endpoint: "CompanionVendor.RfcommEndpoint"): self._endpoints.append(endpoint) def __init__(self, vendorName: str = "", vendorId: str = "", protocolVersion: int = 1, endpoints: List[RfcommEndpoint] = None, transport: Optional["CompanionVendor.Transport"] = None): super().__init__(transportType=COMPANION_TRANSPORT_VENDOR, name=vendorName) self._vendor_id = vendorId self._protocol_version = protocolVersion self._endpoints = endpoints or [] self._transport_layer = transport def getVendorId(self) -> str: return self._vendor_id def getProtocolVersion(self) -> int: return self._protocol_version def getEndpoints(self) -> List["CompanionVendor.RfcommEndpoint"]: return list(self._endpoints) def getTransportLayer(self) -> Optional["CompanionVendor.Transport"]: return self._transport_layer def toString(self) -> str: return f"CompanionVendor(name={self._name}, vendorId={self._vendor_id})" # ═══════════════════════════════════════════════════════════════ # CompanionManager # ═══════════════════════════════════════════════════════════════ class CompanionManager(Stub): """CompanionManager — 配套设备管理器""" def __init__(self): super().__init__() self._associations: Dict[str, AssociationRequest] = {} self._transports: Dict[int, CompanionTransport] = {} self._presence_notifier = DevicePresenceNotifier() self._callbacks: Dict[int, Callable] = {} def associate(self, request: AssociationRequest, callback: Callable, handler: Any = None): raise AndroidOnlyError("CompanionManager.associate() is not available on desktop") def associateCallback(self, request: AssociationRequest, callback: Callable, executor: Any = None): raise AndroidOnlyError("CompanionManager.associateCallback() is not available on desktop") def revokeAssociation(self, deviceAddress: str): self._associations.pop(deviceAddress, None) def getAssociations(self) -> List[AssociationRequest]: return list(self._associations.values()) def isDeviceAssociated(self, deviceAddress: str) -> bool: return deviceAddress in self._associations def registerTransport(self, transport: CompanionTransport): self._transports[transport.getTransportType()] = transport def unregisterTransport(self, transport: CompanionTransport): self._transports.pop(transport.getTransportType(), None) def getTransport(self, transportType: int) -> Optional[CompanionTransport]: return self._transports.get(transportType) def getPresenceNotifier(self) -> DevicePresenceNotifier: return self._presence_notifier def registerDevicePresenceCallback(self, callback: Callable): self._presence_notifier.registerCallback(callback) def unregisterDevicePresenceCallback(self, callback: Callable): self._presence_notifier.unregisterCallback(callback) def startObservingDevicePresence(self, deviceAddress: str): pass def stopObservingDevicePresence(self, deviceAddress: str): pass def toString(self) -> str: return (f"CompanionManager(associations={len(self._associations)}, " f"transports={len(self._transports)})") def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # CompanionDeviceManager (扩展) # ═══════════════════════════════════════════════════════════════ class CompanionDeviceManager(Stub): """CompanionDeviceManager — 配套设备管理器 (扩展)""" EXTRA_DEVICE = "android.companion.extra.DEVICE" EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION" EXTRA_EVENT = "android.companion.extra.EVENT" EXTRA_EVENT_TYPE = "android.companion.extra.EVENT_TYPE" FLAG_SELF_MANAGED = DEVICE_FLAG_SELF_MANAGED FLAG_NOT_ASSOCIATED = DEVICE_FLAG_NOT_ASSOCIATED def __init__(self): super().__init__() self._manager = CompanionManager() def associate(self, request: AssociationRequest, callback: Callable, handler: Any = None): raise AndroidOnlyError("CompanionDeviceManager.associate() is not available on desktop") def associateCallback(self, request: AssociationRequest, callback: Callable, executor: Any = None): raise AndroidOnlyError("CompanionDeviceManager.associateCallback() is not available on desktop") def requestNotificationAccess(self, pairingDevice: Any): raise AndroidOnlyError("CompanionDeviceManager.requestNotificationAccess() is not available on desktop") def requestSystemAppStore(self, pairingDevice: Any): pass def startObservingDevicePresence(self, deviceAddress: str): self._manager.startObservingDevicePresence(deviceAddress) def stopObservingDevicePresence(self, deviceAddress: str): self._manager.stopObservingDevicePresence(deviceAddress) def isDeviceAssociated(self, deviceAddress: str) -> bool: return self._manager.isDeviceAssociated(deviceAddress) def getDeviceId(self, deviceAddress: str) -> Optional[str]: return deviceAddress def getAssociations(self) -> List[Any]: return self._manager.getAssociations() def removeAssociation(self, deviceAddress: str): self._manager.revokeAssociation(deviceAddress) def registerDevicePresenceCallback(self, callback: Callable): self._manager.registerDevicePresenceCallback(callback) def unregisterDevicePresenceCallback(self, callback: Callable): self._manager.unregisterDevicePresenceCallback(callback) def getCompanionManager(self) -> CompanionManager: return self._manager class Callback(Stub): """CompanionDeviceManager.Callback — 设备发现回调""" def onDeviceFound(self, intent: Any): pass def onFailure(self, error: str): pass def toString(self) -> str: return f"Callback@{hex(id(self))}" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF def toString(self) -> str: return f"CompanionDeviceManager(mgr={self._manager})" def equals(self, other: Any) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF