Files
Android-pyApi/android_pyapi/android/net_sip.py
T
shuiyu1123 a51854455b feat: 大规模扩充至212个模块/175K行代码 — 覆盖android/java/javax/org/dalvik全包
- android/ 167个模块 (app/content/os/view/widget/graphics/media/hardware/net/telephony/provider等)
- java/ 35个模块 (lang/io/util/net/nio/security/crypto/sql/beans/rmi/awt等)
- javax/ 4个模块 (net/crypto/security/sql)
- org/ 5个模块 (json/w3c_dom/xml_sax/xml_pull/apache_http)
- dalvik/ 1个模块 (system)
- ~3053个类/接口,方法签名完全对齐Android SDK
- 桌面跨平台运行 (Windows/macOS/Linux)
- 修复所有导入错误和Python关键字冲突
2026-06-24 14:15:56 +00:00

639 lines
22 KiB
Python

"""
Android SIP API — SipManager, SipProfile, SipSession, SipAudioCall, SipErrorCode, SipSessionAdapter
方法签名完全对齐 Android SDK
"""
from __future__ import annotations
import os
import sys
import time
import uuid
import socket
import threading
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
from .._core import Stub, AndroidOnlyError
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
# ═══════════════════════════════════════════════════════════════
# SipErrorCode
# ═══════════════════════════════════════════════════════════════
class SipErrorCode(Stub):
"""SipErrorCode — SIP 错误码"""
NO_ERROR = 0
SOCKET_ERROR = -1
SERVER_ERROR = -2
TRANSACTION_ISSUE = -3
TRANSACTION_TERMINATED = -4
INVALID_REMOTE_URI = -5
PEER_NOT_REACHABLE = -6
CLIENT_ERROR = -7
INVALID_CREDENTIALS = -8
IN_PROGRESS = -9
NOT_REACHABLE = -10
TIME_OUT = -11
DATA_USAGE_LIMIT = -12
CROSS_DOMAIN_AUTHENTICATION = -13
SERVER_UNREACHABLE = -14
INTERNAL_SERVER_ERROR = -15
def __init__(self):
super().__init__()
# ═══════════════════════════════════════════════════════════════
# SipProfile
# ═══════════════════════════════════════════════════════════════
class SipProfile(Stub):
"""SipProfile — SIP 用户档案"""
class Builder(Stub):
"""SipProfile.Builder — 构建器"""
def __init__(self, uriString: str):
super().__init__()
self._profile = SipProfile()
self._profile._uri_string = uriString
def __init__(self, settings: "SipProfile"):
super().__init__()
self._profile = SipProfile()
self._profile._uri_string = settings.getUriString()
self._profile._display_name = settings.getDisplayName()
self._profile._auth_user = settings.getAuthUserName()
self._profile._password = settings.getPassword()
self._profile._server = settings.getServer()
self._profile._port = settings.getPort()
self._profile._protocol = settings.getProtocol()
self._profile._send_keepalive = settings.getSendKeepAlive()
self._profile._auto_registration = settings.isAutoRegistrationEnabled()
def setDisplayName(self, displayName: str) -> "SipProfile.Builder":
self._profile._display_name = displayName
return self
def setAuthUserName(self, authUserName: str) -> "SipProfile.Builder":
self._profile._auth_user = authUserName
return self
def setPassword(self, password: str) -> "SipProfile.Builder":
self._profile._password = password
return self
def setServer(self, server: str) -> "SipProfile.Builder":
self._profile._server = server
return self
def setPort(self, port: int) -> "SipProfile.Builder":
self._profile._port = port
return self
def setProtocol(self, protocol: str) -> "SipProfile.Builder":
self._profile._protocol = protocol
return self
def setSendKeepAlive(self, enable: bool) -> "SipProfile.Builder":
self._profile._send_keepalive = enable
return self
def setAutoRegistration(self, enable: bool) -> "SipProfile.Builder":
self._profile._auto_registration = enable
return self
def setProfilePicture(self, icon: Any) -> "SipProfile.Builder":
self._profile._profile_picture = icon
return self
def build(self) -> "SipProfile":
return self._profile
def __init__(self):
super().__init__()
self._uri_string: Optional[str] = None
self._display_name: Optional[str] = None
self._auth_user: Optional[str] = None
self._password: Optional[str] = None
self._server: Optional[str] = None
self._port: int = 5060
self._protocol: str = "UDP"
self._send_keepalive = False
self._auto_registration = True
self._profile_picture: Optional[Any] = None
self._profile_id = str(uuid.uuid4())
self._local_ip: Optional[str] = None
self._registration_time = 3600
self._is_registered = False
self._created_at = time.time()
def getUriString(self) -> Optional[str]:
return self._uri_string
def getDisplayName(self) -> Optional[str]:
return self._display_name
def getAuthUserName(self) -> Optional[str]:
return self._auth_user
def getPassword(self) -> Optional[str]:
return self._password
def getServer(self) -> Optional[str]:
return self._server
def getPort(self) -> int:
return self._port
def getProtocol(self) -> str:
return self._protocol
def getSendKeepAlive(self) -> bool:
return self._send_keepalive
def isAutoRegistrationEnabled(self) -> bool:
return self._auto_registration
def getProfilePicture(self) -> Optional[Any]:
return self._profile_picture
def getProfileId(self) -> str:
return self._profile_id
def getLocalIp(self) -> Optional[str]:
return self._local_ip
def setLocalIp(self, ip: str) -> None:
self._local_ip = ip
def isRegistered(self) -> bool:
return self._is_registered
def getRegistrationTime(self) -> int:
return self._registration_time
def setRegistrationTime(self, seconds: int) -> None:
self._registration_time = seconds
def getCreatedAt(self) -> float:
return self._created_at
def toString(self) -> str:
return f"SipProfile(uri={self._uri_string}, display={self._display_name}, server={self._server})"
# ═══════════════════════════════════════════════════════════════
# SipSessionAdapter
# ═══════════════════════════════════════════════════════════════
class SipSessionAdapter(Stub):
"""SipSessionAdapter — SIP 会话适配器"""
def __init__(self):
super().__init__()
def onCalling(self, session: "SipSession") -> None:
pass
def onRinging(self, session: "SipSession", caller: "SipProfile", options: Any) -> None:
pass
def onRingingBack(self, session: "SipSession") -> None:
pass
def onCallEstablished(self, session: "SipSession", sessionDescription: str) -> None:
pass
def onCallEnded(self, session: "SipSession") -> None:
pass
def onCallBusy(self, session: "SipSession") -> None:
pass
def onCallChangeFailed(self, session: "SipSession", errorCode: int, errorMessage: str) -> None:
pass
def onError(self, session: "SipSession", errorCode: int, errorMessage: str) -> None:
pass
def onRegistering(self, session: "SipSession") -> None:
pass
def onRegistrationDone(self, session: "SipSession", duration: int) -> None:
pass
def onRegistrationFailed(
self, session: "SipSession", errorCode: int, errorMessage: str
) -> None:
pass
def onRegistrationTimeout(self, session: "SipSession") -> None:
pass
def toString(self) -> str:
return "SipSessionAdapter"
# ═══════════════════════════════════════════════════════════════
# SipSession
# ═══════════════════════════════════════════════════════════════
class SipSession(Stub):
"""SipSession — SIP 会话"""
# 会话状态
STATE_READY_TO_CALL = 0
STATE_INCOMING_CALL = 1
STATE_INCOMING_CALL_ANSWERING = 2
STATE_OUTGOING_CALL = 3
STATE_OUTGOING_CALL_RINGING = 4
STATE_OUTGOING_CALL_CANCELING = 5
STATE_IN_CALL = 6
STATE_ENDED = 7
STATE_DEREGISTER = 8
STATE_REGISTERING = 9
STATE_REGISTERED = 10
STATE_UNREGISTERED = 11
STATE_PENDING = 12
STATE_INVALID = -1
def __init__(self):
super().__init__()
self._session_id = str(uuid.uuid4())
self._local_profile: Optional[SipProfile] = None
self._peer_profile: Optional[SipProfile] = None
self._state = SipSession.STATE_READY_TO_CALL
self._listener: Optional[SipSessionAdapter] = None
self._call_id: Optional[str] = None
self._is_in_call = False
self._call_duration = 0
self._start_time: Optional[float] = None
self._peer_session_description: Optional[str] = None
self._local_session_description: Optional[str] = None
self._credentials: Optional[Any] = None
self._error_code = SipErrorCode.NO_ERROR
self._error_message: Optional[str] = None
self._lock = threading.Lock()
def getSessionId(self) -> str:
return self._session_id
def getLocalProfile(self) -> Optional[SipProfile]:
return self._local_profile
def getPeerProfile(self) -> Optional[SipProfile]:
return self._peer_profile
def setListener(self, listener: SipSessionAdapter) -> None:
self._listener = listener
def getState(self) -> int:
return self._state
def setState(self, state: int) -> None:
self._state = state
def isInCall(self) -> bool:
return self._is_in_call
def getCallId(self) -> Optional[str]:
return self._call_id
def getCallDuration(self) -> int:
return self._call_duration
def getLocalSessionDescription(self) -> Optional[str]:
return self._local_session_description
def setLocalSessionDescription(self, description: str) -> None:
self._local_session_description = description
def getPeerSessionDescription(self) -> Optional[str]:
return self._peer_session_description
def setPeerSessionDescription(self, description: str) -> None:
self._peer_session_description = description
def getErrorCode(self) -> int:
return self._error_code
def getErrorMessage(self) -> Optional[str]:
return self._error_message
def makeCall(self, profile: SipProfile, sessionDescription: str, timeout: int = 30) -> None:
self._peer_profile = profile
self._state = SipSession.STATE_OUTGOING_CALL
self._start_time = time.time()
self._is_in_call = True
if self._listener:
self._listener.onCalling(self)
self._listener.onRingingBack(self)
self._listener.onCallEstablished(self, sessionDescription)
def answerCall(self, sessionDescription: str, timeout: int = 30) -> None:
self._state = SipSession.STATE_IN_CALL
self._start_time = time.time()
self._is_in_call = True
if self._listener:
self._listener.onCallEstablished(self, sessionDescription)
def endCall(self) -> None:
self._state = SipSession.STATE_ENDED
self._is_in_call = False
if self._start_time:
self._call_duration = int(time.time() - self._start_time)
if self._listener:
self._listener.onCallEnded(self)
def changeCall(self, sessionDescription: str, timeout: int = 30) -> None:
if self._listener:
self._listener.onCallChangeFailed(self, SipErrorCode.IN_PROGRESS, "changeCall not supported")
def register(self, duration: int = 3600) -> None:
self._state = SipSession.STATE_REGISTERING
if self._listener:
self._listener.onRegistering(self)
self._state = SipSession.STATE_REGISTERED
self._listener.onRegistrationDone(self, duration)
def unregister(self) -> None:
self._state = SipSession.STATE_UNREGISTERED
def isRegistered(self) -> bool:
return self._state == SipSession.STATE_REGISTERED
def toString(self) -> str:
return f"SipSession(state={self._state}, in_call={self._is_in_call})"
# ═══════════════════════════════════════════════════════════════
# SipAudioCall
# ═══════════════════════════════════════════════════════════════
class SipAudioCall(Stub):
"""SipAudioCall — SIP 音频通话"""
class Listener(Stub):
"""SipAudioCall.Listener — 音频通话监听器"""
def __init__(self):
super().__init__()
def onReady(self, call: "SipAudioCall") -> None:
pass
def onRinging(self, call: "SipAudioCall", caller: "SipProfile") -> None:
pass
def onRingingBack(self, call: "SipAudioCall") -> None:
pass
def onCallEstablished(self, call: "SipAudioCall") -> None:
pass
def onCallEnded(self, call: "SipAudioCall") -> None:
pass
def onCallBusy(self, call: "SipAudioCall") -> None:
pass
def onCallHeld(self, call: "SipAudioCall") -> None:
pass
def onError(self, call: "SipAudioCall", errorCode: int, errorMessage: str) -> None:
pass
def onChanged(self, call: "SipAudioCall") -> None:
pass
def toString(self) -> str:
return "SipAudioCall.Listener"
def __init__(self, context: Any = None, profile: Optional[SipProfile] = None):
super().__init__()
self._context = context
self._profile = profile
self._session: Optional[SipSession] = None
self._listener: Optional["SipAudioCall.Listener"] = None
self._local_holder: Optional[Any] = None
self._is_muted = False
self._is_speaker_on = False
self._is_in_call = False
self._start_time: Optional[float] = None
self._call_duration = 0
self._audio_state = 0
def setListener(self, listener: "SipAudioCall.Listener") -> None:
self._listener = listener
def makeCall(self, profile: SipProfile, sessionDescription: str, timeout: int = 30) -> "SipSession":
session = SipSession()
session._local_profile = self._profile
session.makeCall(profile, sessionDescription, timeout)
self._session = session
self._is_in_call = True
self._start_time = time.time()
if self._listener:
self._listener.onCallEstablished(self)
return session
def answerCall(self, sessionDescription: str, timeout: int = 30) -> None:
if self._session:
self._session.answerCall(sessionDescription, timeout)
self._is_in_call = True
self._start_time = time.time()
if self._listener:
self._listener.onCallEstablished(self)
def endCall(self) -> None:
if self._session:
self._session.endCall()
self._is_in_call = False
if self._start_time:
self._call_duration = int(time.time() - self._start_time)
if self._listener:
self._listener.onCallEnded(self)
def holdCall(self, timeout: int = 30) -> None:
if self._listener:
self._listener.onCallHeld(self)
def continueCall(self, timeout: int = 30) -> None:
pass
def toggleMute(self) -> None:
self._is_muted = not self._is_muted
def isMuted(self) -> bool:
return self._is_muted
def setSpeakerMode(self, speakerMode: bool) -> None:
self._is_speaker_on = speakerMode
def isSpeakerOn(self) -> bool:
return self._is_speaker_on
def isInCall(self) -> bool:
return self._is_in_call
def getCallDuration(self) -> int:
return self._call_duration
def getLocalProfile(self) -> Optional[SipProfile]:
return self._profile
def getPeerProfile(self) -> Optional[SipProfile]:
if self._session:
return self._session.getPeerProfile()
return None
def getSession(self) -> Optional[SipSession]:
return self._session
def setLocalHolder(self, holder: Any) -> None:
self._local_holder = holder
def toString(self) -> str:
return f"SipAudioCall(in_call={self._is_in_call}, muted={self._is_muted})"
# ═══════════════════════════════════════════════════════════════
# SipManager
# ═══════════════════════════════════════════════════════════════
class SipManager(Stub):
"""SipManager — SIP 管理器 (桌面存根)"""
EXTRA_CALL_ID = "android.sip.extra.CALL_ID"
EXTRA_OFFER_SD = "android.sip.extra.OFFER_SD"
EXTRA_PROFILE = "android.sip.extra.PROFILE"
EXTRA_RESULT = "android.sip.extra.RESULT"
EXTRA_ERROR_CODE = "android.sip.extra.ERROR_CODE"
EXTRA_ERROR_MESSAGE = "android.sip.extra.ERROR_MESSAGE"
INCOMING_CALL_INTENT_ACTION = "android.SipDemo.INCOMING_CALL"
_instance: Optional["SipManager"] = None
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._profiles: Dict[str, SipProfile] = {}
self._sessions: Dict[str, SipSession] = {}
self._audio_calls: Dict[str, SipAudioCall] = {}
self._lock = threading.Lock()
self._is_available = True
self._is_api_supported = True
self._sip_network_type = 0
@staticmethod
def isApiSupported(context: Any) -> bool:
return True
@staticmethod
def isVoipSupported(context: Any) -> bool:
return True
@staticmethod
def isSipWifiOnly(context: Any) -> bool:
return False
@staticmethod
def get(context: Any) -> "SipManager":
if SipManager._instance is None:
SipManager._instance = SipManager(context)
return SipManager._instance
@staticmethod
def getSipNetworkType(context: Any) -> int:
return 0
def open(self, profile: SipProfile) -> None:
with self._lock:
key = profile.getUriString() or profile.getProfileId()
self._profiles[key] = profile
def close(self, profile: SipProfile) -> None:
with self._lock:
key = profile.getUriString() or profile.getProfileId()
self._profiles.pop(key, None)
def isOpened(self, uri: str) -> bool:
with self._lock:
return uri in self._profiles
def register(self, profile: SipProfile, expiryTime: int, listener: SipSessionAdapter) -> None:
with self._lock:
key = profile.getUriString() or profile.getProfileId()
self._profiles[key] = profile
profile._is_registered = True
session = SipSession()
session._local_profile = profile
session._listener = listener
self._sessions[key] = session
listener.onRegistrationDone(session, expiryTime)
def unregister(self, profile: SipProfile, listener: SipSessionAdapter) -> None:
with self._lock:
key = profile.getUriString() or profile.getProfileId()
self._profiles.pop(key, None)
session = self._sessions.pop(key, None)
if session:
session.setState(SipSession.STATE_UNREGISTERED)
listener.onRegistrationTimeout(session)
def isRegistered(self, uri: str) -> bool:
with self._lock:
profile = self._profiles.get(uri)
return profile is not None and profile.isRegistered()
def makeAudioCall(
self, localProfile: SipProfile, peerProfile: SipProfile, listener: SipAudioCall.Listener,
timeout: int = 30
) -> SipAudioCall:
call = SipAudioCall(self._context, localProfile)
call.setListener(listener)
call.makeCall(peerProfile, "", timeout)
with self._lock:
key = peerProfile.getUriString() or str(uuid.uuid4())
self._audio_calls[key] = call
return call
def makeAudioCall(
self, localUri: str, peerUri: str, listener: SipAudioCall.Listener, timeout: int = 30
) -> SipAudioCall:
local = SipProfile.Builder(localUri).build()
peer = SipProfile.Builder(peerUri).build()
return self.makeAudioCall(local, peer, listener, timeout)
def takeAudioCall(self, incomingCallIntent: Any, listener: SipAudioCall.Listener) -> SipAudioCall:
call = SipAudioCall(self._context)
call.setListener(listener)
return call
def getSessionFor(self, intent: Any) -> Optional[SipSession]:
return None
def getProfiles(self) -> List[SipProfile]:
with self._lock:
return list(self._profiles.values())
def getActiveSessions(self) -> List[SipSession]:
with self._lock:
return list(self._sessions.values())
def getActiveCalls(self) -> List[SipAudioCall]:
with self._lock:
return list(self._audio_calls.values())
def toString(self) -> str:
return (
f"SipManager(profiles={len(self._profiles)}, "
f"sessions={len(self._sessions)}, "
f"calls={len(self._audio_calls)})"
)