""" Android se 组件 — SecureElement, Session, Channel, Reader, OmapiConnection 等 方法签名完全对齐 Android SDK (SecureElement API) """ from typing import Any, Callable, Dict, List, Optional from .._core import Stub, AndroidOnlyError # ═══════════════════════════════════════════════════════════════ # SecureElement # ═══════════════════════════════════════════════════════════════ class SecureElement(Stub): """SecureElement — 安全元件""" SERVICE_INTERFACE = "android.se.SECURE_ELEMENT_SERVICE" EXTRA_READER = "android.se.extra.READER" EXTRA_READER_NAME = "android.se.extra.READER_NAME" EXTRA_SLOT_ID = "android.se.extra.SLOT_ID" EXTRA_CONNECT_RESPONSE = "android.se.extra.CONNECT_RESPONSE" def __init__(self): super().__init__() # ═══════════════════════════════════════════════════════════════ # Reader # ═══════════════════════════════════════════════════════════════ class Reader(Stub): """Reader — 安全元件读取器""" def __init__(self, name: str = "eSE"): super().__init__() self._name = name self._sessions: List[Session] = [] def getName(self) -> str: return self._name def openSession(self) -> "Session": session = Session(self) self._sessions.append(session) return session def closeSessions(self): for s in self._sessions: s.close() self._sessions.clear() def isSecureElementPresent(self) -> bool: return True def toString(self) -> str: return f"Reader(name={self._name})" # ═══════════════════════════════════════════════════════════════ # Session # ═══════════════════════════════════════════════════════════════ class Session(Stub): """Session — 安全元件会话""" def __init__(self, reader: Optional[Reader] = None): super().__init__() self._reader = reader self._channels: List[Channel] = [] self._closed = False def openLogicalChannel(self, aid: bytes) -> Optional["Channel"]: if self._closed: return None channel = Channel(self, logical=True) self._channels.append(channel) channel._select_response = bytes([0x90, 0x00]) # SW_OK return channel def openBasicChannel(self, aid: bytes) -> Optional["Channel"]: if self._closed: return None channel = Channel(self, logical=False) self._channels.append(channel) channel._select_response = bytes([0x90, 0x00]) # SW_OK return channel def close(self): for c in self._channels: c.close() self._channels.clear() self._closed = True def getSessions(self) -> List["Session"]: return [self] def getChannels(self) -> List["Channel"]: return list(self._channels) def toString(self) -> str: return f"Session(reader={self._reader}, channels={len(self._channels)})" # ═══════════════════════════════════════════════════════════════ # Channel # ═══════════════════════════════════════════════════════════════ class Channel(Stub): """Channel — 安全元件通道""" def __init__(self, session: Session, logical: bool = True): super().__init__() self._session = session self._logical = logical self._closed = False self._select_response: Optional[bytes] = None def close(self): self._closed = True def sendData(self, command: bytes) -> bytes: if self._closed: return bytes([0x6F, 0x00]) # No such EF # Simulate successful APDU response return bytes([0x90, 0x00]) def getSelectResponse(self) -> Optional[bytes]: return self._select_response def getSession(self) -> Session: return self._session def isBasicChannel(self) -> bool: return not self._logical def isClosed(self) -> bool: return self._closed def toString(self) -> str: return f"Channel(logical={self._logical}, closed={self._closed})" # ═══════════════════════════════════════════════════════════════ # OmapiConnection # ═══════════════════════════════════════════════════════════════ class OmapiConnection(Stub): """OmapiConnection — OMAPI 连接入口""" def __init__(self): super().__init__() @staticmethod def getInstance(context: Any = None) -> "OmapiConnection": return OmapiConnection() def getReaders(self) -> List[Reader]: return [Reader("eSE")] def getReader(self, name: str) -> Optional[Reader]: return Reader(name) # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "SecureElement", "Reader", "Session", "Channel", "OmapiConnection", ]