""" Android Telephony MBMS 组件 — MbmsDownloadManager, MbmsStreamingManager, DownloadRequest, StreamingService 等 方法签名完全对齐 Android SDK (android.telephony.mbms 包) """ from __future__ import annotations import os import time from typing import Any, Callable, Dict, List, Optional, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # MbmsErrors # ═══════════════════════════════════════════════════════════════ class MbmsErrors(Stub): """MbmsErrors — MBMS 错误代码基类""" SUCCESS = 0 ERROR_NO_UNIQUE_MIDDLEWARE = 1 ERROR_MIDDLEWARE_NOT_BOUND = 2 ERROR_MIDDLEWARE_LOST = 3 ERROR_MIDDLEWARE_NOT_YET_READY = 4 ERROR_OUT_OF_MEMORY = 5 ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 6 ERROR_MIDDLEWARE_PERMANENTLY_UNAVAILABLE = 7 ERROR_MIDDLEWARE_UNABLE_TO_CREATE_SESSION = 8 ERROR_MIDDLEWARE_UNABLE_TO_CREATE_DATA_SESSION = 9 ERROR_MIDDLEWARE_UNABLE_TO_CREATE_DOWNLOAD = 10 ERROR_MIDDLEWARE_UNABLE_TO_CREATE_STREAMING = 11 ERROR_MIDDLEWARE_INTERNAL_ERROR = 12 ERROR_MIDDLEWARE_NOT_SUPPORTED = 13 ERROR_MIDDLEWARE_INVALID_INPUT = 14 ERROR_MIDDLEWARE_INVALID_SERVICE_ID = 15 ERROR_MIDDLEWARE_INVALID_FILE_PATH = 16 ERROR_MIDDLEWARE_DUPLICATE_SERVICE = 17 ERROR_MIDDLEWARE_SERVICE_NOT_FOUND = 18 ERROR_MIDDLEWARE_SERVICE_ALREADY_RUNNING = 19 ERROR_MIDDLEWARE_SERVICE_NOT_RUNNING = 20 ERROR_MIDDLEWARE_INVALID_SAI = 21 ERROR_MIDDLEWARE_INVALID_CELL_ID = 22 ERROR_MIDDLEWARE_UNABLE_TO_FETCH_PREFIX = 23 ERROR_MIDDLEWARE_TEMP_FILE_EXPIRED = 24 ERROR_MIDDLEWARE_FILE_ALREADY_COMPLETE = 25 ERROR_MIDDLEWARE_FILE_ALREADY_IN_PROGRESS = 26 ERROR_MIDDLEWARE_STALE_SERVICE_ANNOUNCEMENT = 27 ERROR_MIDDLEWARE_MISSING_FILE_INFO = 28 ERROR_MIDDLEWARE_NO_EXPIRATION = 29 ERROR_MIDDLEWARE_UNKNOWN_FILE_INFO = 30 ERROR_MIDDLEWARE_UNKNOWN_SIGNAL = 31 ERROR_MIDDLEWARE_INSUFFICIENT_STORAGE = 32 ERROR_MIDDLEWARE_INSUFFICIENT_MEMORY = 33 ERROR_MIDDLEWARE_UNABLE_TO_CLOSE_FILE = 34 ERROR_MIDDLEWARE_UNABLE_TO_OPEN_FILE = 35 ERROR_MIDDLEWARE_IO_ERROR = 36 ERROR_MIDDLEWARE_CONCURRENT_SERVICE_LIMIT = 37 ERROR_MIDDLEWARE_INVALID_DOWNLOAD_ID = 38 class InitializationErrors(Stub): """MbmsErrors.InitializationErrors — 初始化错误""" ERROR_DUPLICATE_INITIALIZE = 101 ERROR_APP_PERMISSIONS_NOT_GRANTED = 102 ERROR_UNABLE_TO_INITIALIZE = 103 ERROR_INTERNAL = 104 ERROR_MIDDLEWARE_NOT_BOUND = 105 class DownloadErrors(Stub): """MbmsErrors.DownloadErrors — 下载错误""" ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 201 ERROR_UNKNOWN_DOWNLOAD_REQUEST = 202 ERROR_DOWNLOAD_NOT_FOUND = 203 ERROR_DOWNLOAD_ALREADY_EXISTS = 204 ERROR_DOWNLOAD_INACTIVE = 205 ERROR_FILE_UNAVAILABLE = 206 ERROR_SERVICE_ID_NOT_DOWNLOAD = 207 ERROR_MALFORMED_FILE_URI = 208 ERROR_STALE_FILE_INFO = 209 ERROR_TEMP_FILE_ROOT_NOT_SET = 210 ERROR_UNKNOWN_FILE_INFO = 211 ERROR_NO_DOWNLOAD_FOR_FILE = 212 class StreamingErrors(Stub): """MbmsErrors.StreamingErrors — 流错误""" ERROR_STREAMING_NOT_ALLOWED = 301 ERROR_STREAMING_SERVICE_NOT_STARTED = 302 ERROR_STREAMING_SERVICE_ALREADY_STARTED = 303 ERROR_STREAMING_SERVICE_NOT_VISIBLE = 304 ERROR_STREAMING_MALFORMED_SERVICE_ANNOUNCEMENT = 305 ERROR_STREAMING_DUPLICATE_SERVICE_ANNOUNCEMENT = 306 ERROR_STREAMING_SERVICE_ANNOUNCEMENT_EXPIRED = 307 ERROR_STREAMING_RADIO_RESOURCE_REJECTED = 308 ERROR_STREAMING_RADIO_RESOURCE_NOT_AVAILABLE = 309 ERROR_STREAMING_INVALID_SAI_LIST = 310 ERROR_STREAMING_UNABLE_TO_UPDATE_SUBFLOW = 311 class NameTypeErrors(Stub): """MbmsErrors.NameTypeErrors — 名称类型错误""" ERROR_NAME_SYSTEM_ERROR = 401 ERROR_NAME_INVALID_FORMAT = 402 ERROR_NAME_UNSUPPORTED_CHARACTER_ENCODING = 403 ERROR_NAME_CONTAINS_INVALID_CHARACTERS = 404 class GeneralErrors(Stub): """MbmsErrors.GeneralErrors — 通用错误""" ERROR_MIDDLEWARE_NOT_BOUND = 501 ERROR_OUT_OF_MEMORY = 502 ERROR_MIDDLEWARE_LOST = 503 ERROR_MIDDLEWARE_NOT_YET_READY = 504 ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 505 ERROR_MIDDLEWARE_UNABLE_TO_CREATE_SESSION = 506 # ═══════════════════════════════════════════════════════════════ # DownloadRequest # ═══════════════════════════════════════════════════════════════ class DownloadRequest(Stub): """DownloadRequest — MBMS 下载请求""" # 下载请求常量 DOWNLOAD_RESULT_SUCCESS = 0 DOWNLOAD_RESULT_ERROR = 1 DOWNLOAD_RESULT_ERROR_MEDIA_NOT_FOUND = 2 DOWNLOAD_RESULT_ERROR_MEDIA_UNAVAILABLE = 3 DOWNLOAD_RESULT_ERROR_MEDIA_FAILED = 4 DOWNLOAD_RESULT_ERROR_SERVICE_UNAVAILABLE = 5 DOWNLOAD_RESULT_ERROR_INSUFFICIENT_STORAGE = 6 DOWNLOAD_RESULT_ERROR_IO_ERROR = 7 DOWNLOAD_RESULT_ERROR_FILE_NOT_FOUND = 8 DOWNLOAD_RESULT_ERROR_OUT_OF_MEMORY = 9 DOWNLOAD_RESULT_ERROR_RADIO_OFF = 10 DOWNLOAD_RESULT_ERROR_INTERRUPTED = 11 DOWNLOAD_RESULT_ERROR_TIMEOUT = 12 DOWNLOAD_RESULT_ERROR_CANCELLED = 13 DOWNLOAD_RESULT_ERROR_DOWNLOAD_NOT_STARTED = 14 # 下载状态 DOWNLOAD_STATUS_INACTIVE = 0 DOWNLOAD_STATUS_ACTIVE = 1 DOWNLOAD_STATUS_PENDING = 2 DOWNLOAD_STATUS_PAUSED = 3 DOWNLOAD_STATUS_COMPLETED = 4 DOWNLOAD_STATUS_CANCELED = 5 DOWNLOAD_STATUS_FAILED = 6 def __init__(self, service_id: str = "", file_uri: str = "", source: Any = None, destination: Any = None, app_intent: Any = None): super().__init__() self._service_id = service_id self._file_uri = file_uri self._source = source self._destination = destination self._app_intent = app_intent self._subscription_id: int = 0 self._sai: List[int] = [] self._app_name: str = "" self._app_version: str = "" self._storage_type: int = 0 self._hash: str = "" self._request_id: int = 0 def getServiceId(self) -> str: return self._service_id def getFileUri(self) -> str: return self._file_uri def getSource(self) -> Any: return self._source def getDestination(self) -> Any: return self._destination def getIntent(self) -> Any: return self._app_intent def getSubscriptionId(self) -> int: return self._subscription_id def setSubscriptionId(self, sub_id: int): self._subscription_id = sub_id def getSai(self) -> List[int]: return list(self._sai) def setSai(self, sai: List[int]): self._sai = list(sai) def getAppName(self) -> str: return self._app_name def setAppName(self, name: str): self._app_name = name def getAppVersion(self) -> str: return self._app_version def setAppVersion(self, version: str): self._app_version = version def getStorageType(self) -> int: return self._storage_type def setStorageType(self, storage_type: int): self._storage_type = storage_type def getHash(self) -> str: return self._hash def getRequestId(self) -> int: return self._request_id def setRequestId(self, request_id: int): self._request_id = request_id def toString(self) -> str: return f"DownloadRequest(serviceId={self._service_id}, fileUri={self._file_uri})" class Builder(Stub): """DownloadRequest.Builder — 下载请求构建器""" def __init__(self, service_id: str, file_uri: str): super().__init__() self._service_id = service_id self._file_uri = file_uri self._source: Any = None self._destination: Any = None self._app_intent: Any = None self._subscription_id: int = 0 self._sai: List[int] = [] self._app_name: str = "" self._app_version: str = "" self._storage_type: int = 0 def setServiceId(self, service_id: str) -> DownloadRequest.Builder: self._service_id = service_id return self def setFileUri(self, file_uri: str) -> DownloadRequest.Builder: self._file_uri = file_uri return self def setSource(self, source: Any) -> DownloadRequest.Builder: self._source = source return self def setDestination(self, destination: Any) -> DownloadRequest.Builder: self._destination = destination return self def setIntent(self, intent: Any) -> DownloadRequest.Builder: self._app_intent = intent return self def setSubscriptionId(self, sub_id: int) -> DownloadRequest.Builder: self._subscription_id = sub_id return self def setSai(self, sai: List[int]) -> DownloadRequest.Builder: self._sai = list(sai) return self def setAppName(self, name: str) -> DownloadRequest.Builder: self._app_name = name return self def setAppVersion(self, version: str) -> DownloadRequest.Builder: self._app_version = version return self def setStorageType(self, storage_type: int) -> DownloadRequest.Builder: self._storage_type = storage_type return self def build(self) -> DownloadRequest: req = DownloadRequest(self._service_id, self._file_uri, self._source, self._destination, self._app_intent) req.setSubscriptionId(self._subscription_id) req.setSai(self._sai) req.setAppName(self._app_name) req.setAppVersion(self._app_version) req.setStorageType(self._storage_type) return req # ═══════════════════════════════════════════════════════════════ # FileInfo # ═══════════════════════════════════════════════════════════════ class FileInfo(Stub): """FileInfo — MBMS 文件信息""" def __init__(self, uri: str = "", mime_type: str = "", file_size: int = 0, name: str = ""): super().__init__() self._uri = uri self._mime_type = mime_type self._file_size = file_size self._name = name def getUri(self) -> str: return self._uri def getMimeType(self) -> str: return self._mime_type def getFileSize(self) -> int: return self._file_size def getName(self) -> str: return self._name def toString(self) -> str: return f"FileInfo(uri={self._uri}, mimeType={self._mime_type}, size={self._file_size})" # ═══════════════════════════════════════════════════════════════ # DownloadStatusListener # ═══════════════════════════════════════════════════════════════ class DownloadStatusListener(Stub): """DownloadStatusListener — MBMS 下载状态监听器""" def onStatusChanged(self, request: DownloadRequest, info: FileInfo, status: int): """下载状态改变回调""" pass # ═══════════════════════════════════════════════════════════════ # FileServiceInfo # ═══════════════════════════════════════════════════════════════ class FileServiceInfo(Stub): """FileServiceInfo — MBMS 文件服务信息""" def __init__(self, service_id: str = "", service_class: str = "", service_announcement: Any = None): super().__init__() self._service_id = service_id self._service_class = service_class self._service_announcement = service_announcement self._sais: List[int] = [] self._cells: List[int] = [] self._files: List[FileInfo] = [] def getServiceId(self) -> str: return self._service_id def getServiceClass(self) -> str: return self._service_class def getServiceAnnouncement(self) -> Any: return self._service_announcement def getSai(self) -> List[int]: return list(self._sais) def getCells(self) -> List[int]: return list(self._cells) def getFiles(self) -> List[FileInfo]: return list(self._files) def toString(self) -> str: return f"FileServiceInfo(serviceId={self._service_id}, class={self._service_class})" # ═══════════════════════════════════════════════════════════════ # StreamingService # ═══════════════════════════════════════════════════════════════ class StreamingService(Stub): """StreamingService — MBMS 流媒体服务""" # 流媒体服务状态 STATE_STOPPED = 0 STATE_STARTED = 1 STATE_STALLED = 2 STATE_PENDING = 3 STATE_ERROR = 4 # 流媒体服务广播标志 BROADCAST_METHOD_UNICAST = 0 BROADCAST_METHOD_MULTICAST = 1 BROADCAST_METHOD_BROADCAST = 2 # 流媒体服务信息 STREAMING_SERVICE_STATE_STOPPED = 0 STREAMING_SERVICE_STATE_STARTED = 1 STREAMING_SERVICE_STATE_STALLED = 2 STREAMING_SERVICE_STATE_PENDING = 3 STREAMING_SERVICE_STATE_ERROR = 4 REASON_NONE = 0 REASON_BY_USER_REQUEST = 1 REASON_BY_MIDDLEWARE = 2 REASON_BY_RADIO_RESOURCE = 3 REASON_BY_OUT_OF_MEMORY = 4 REASON_BY_SIGNAL_LOST = 5 REASON_BY_TEMPORARILY_UNAVAILABLE = 6 REASON_BY_PERMANENTLY_UNAVAILABLE = 7 REASON_BY_SERVICE_SHUTDOWN = 8 REASON_BY_SERVICE_NOT_VISIBLE = 9 REASON_BY_RADIO_NOT_AVAILABLE = 10 def __init__(self, service_id: str = "", service_class: str = ""): super().__init__() self._service_id = service_id self._service_class = service_class self._state = StreamingService.STATE_STOPPED self._broadcast_method = StreamingService.BROADCAST_METHOD_MULTICAST self._callback: Optional[StreamingServiceCallback] = None self._audio_description: Any = None self._video_description: Any = None self._subtitle_descriptions: List[Any] = [] self._estimated_bitrate: int = 0 def getServiceId(self) -> str: return self._service_id def getServiceClass(self) -> str: return self._service_class def getState(self) -> int: return self._state def setState(self, state: int): self._state = state def getBroadcastMethod(self) -> int: return self._broadcast_method def setBroadcastMethod(self, method: int): self._broadcast_method = method def getAudioDescription(self) -> Any: return self._audio_description def setAudioDescription(self, desc: Any): self._audio_description = desc def getVideoDescription(self) -> Any: return self._video_description def setVideoDescription(self, desc: Any): self._video_description = desc def getSubtitleDescriptions(self) -> List[Any]: return list(self._subtitle_descriptions) def setSubtitleDescriptions(self, descs: List[Any]): self._subtitle_descriptions = list(descs) def getEstimatedBitrate(self) -> int: return self._estimated_bitrate def setEstimatedBitrate(self, bitrate: int): self._estimated_bitrate = bitrate def setCallback(self, callback: StreamingServiceCallback): self._callback = callback def getCallback(self) -> Optional[StreamingServiceCallback]: return self._callback def start(self): """启动流媒体服务""" self._state = StreamingService.STATE_STARTED if self._callback: self._callback.onStreamStateUpdated(self._state, StreamingService.REASON_BY_USER_REQUEST) def stop(self): """停止流媒体服务""" self._state = StreamingService.STATE_STOPPED if self._callback: self._callback.onStreamStateUpdated(self._state, StreamingService.REASON_BY_USER_REQUEST) def close(self): """关闭流媒体服务""" self._state = StreamingService.STATE_STOPPED self._callback = None def toString(self) -> str: return f"StreamingService(serviceId={self._service_id}, state={self._state})" # ═══════════════════════════════════════════════════════════════ # StreamingServiceCallback # ═══════════════════════════════════════════════════════════════ class StreamingServiceCallback(Stub): """StreamingServiceCallback — MBMS 流媒体服务回调""" SIGNAL_STRENGTH_UNKNOWN = 0 SIGNAL_STRENGTH_POOR = 1 SIGNAL_STRENGTH_MODERATE = 2 SIGNAL_STRENGTH_GOOD = 3 SIGNAL_STRENGTH_GREAT = 4 def onStreamStateUpdated(self, state: int, reason: int): """流状态更新回调""" pass def onMediaDescriptionUpdated(self): """媒体描述更新回调""" pass def onBroadcastSignalStrengthUpdated(self, signal_strength: int): """广播信号强度更新回调""" pass def onStreamMethodUpdated(self, broadcast_method: int): """流方法更新回调""" pass def onError(self, error_code: int, message: str = ""): """错误回调""" pass # ═══════════════════════════════════════════════════════════════ # DownloadProgressListener # ═══════════════════════════════════════════════════════════════ class DownloadProgressListener(Stub): """DownloadProgressListener — MBMS 下载进度监听器""" def onProgressUpdated(self, request: DownloadRequest, info: FileInfo, current_bytes: int, total_bytes: int, current_files: int, total_files: int): """下载进度更新回调""" pass # ═══════════════════════════════════════════════════════════════ # DownloadRequestListener # ═══════════════════════════════════════════════════════════════ class DownloadRequestListener(Stub): """DownloadRequestListener — MBMS 下载请求监听器""" def onProgressUpdated(self, request: DownloadRequest, info: FileInfo, current_bytes: int, total_bytes: int, current_files: int, total_files: int): pass def onDownloadStateChanged(self, request: DownloadRequest, state: int): pass # ═══════════════════════════════════════════════════════════════ # MbmsDownloadSession (pre-API 30) # ═══════════════════════════════════════════════════════════════ class MbmsDownloadSession(Stub): """MbmsDownloadSession — MBMS 下载会话 (旧版 API)""" EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.mbms.extra.MBMS_DOWNLOAD_RESULT" EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.mbms.extra.MBMS_DOWNLOAD_REQUEST" EXTRA_MBMS_FILE_INFO = "android.telephony.mbms.extra.MBMS_FILE_INFO" EXTRA_MBMS_COMPLETED_FILES = "android.telephony.mbms.extra.MBMS_COMPLETED_FILES" RESULT_SUCCESSFUL_DOWNLOAD = 0 RESULT_EXPIRED_CHARGES = 1 RESULT_FILE_UNAVAILABLE = 2 RESULT_IO_ERROR = 3 RESULT_OUT_OF_STORAGE = 4 RESULT_SERVICE_NOT_DOWNLOAD = 5 RESULT_DOWNLOAD_NOT_STARTED = 6 RESULT_FILE_NOT_FOUND = 7 RESULT_FILE_IN_PROGRESS = 8 RESULT_FILE_ALREADY_DOWNLOADED = 9 RESULT_MALFORMED_URI = 10 RESULT_CANCELLED = 11 RESULT_INTERRUPTED = 12 RESULT_TIMEOUT = 13 RESULT_SERVICE_UNAVAILABLE = 14 RESULT_MEDIA_NOT_FOUND = 15 RESULT_MEDIA_FAILED = 16 RESULT_ERROR_RADIO_OFF = 17 RESULT_ERROR_OUT_OF_MEMORY = 18 _instance_map: Dict[int, MbmsDownloadSession] = {} def __init__(self, context: Any = None, subscription_id: int = 0, callback: Any = None): super().__init__() self._context = context self._subscription_id = subscription_id self._callback = callback self._middleware_bound = False self._download_requests: Dict[int, DownloadRequest] = {} self._file_services: Dict[str, FileServiceInfo] = {} self._temp_root: str = "" @staticmethod def create(context: Any, callback: Any, subscription_id: int = 0) -> MbmsDownloadSession: session = MbmsDownloadSession(context, subscription_id, callback) MbmsDownloadSession._instance_map[subscription_id] = session return session def requestUpdate(self, file_service_list: List[FileServiceInfo]): """请求更新文件服务列表""" pass def download(self, request: DownloadRequest): """开始下载""" request_id = int(time.time() * 1000) request.setRequestId(request_id) self._download_requests[request_id] = request def cancelDownload(self, download_request: DownloadRequest): """取消下载""" rid = download_request.getRequestId() if rid in self._download_requests: del self._download_requests[rid] def getCurrentPendingDownloadRequests(self) -> List[DownloadRequest]: """获取当前待处理的下载请求""" return list(self._download_requests.values()) def getTempFileRoot(self) -> str: """获取临时文件根目录""" return self._temp_root def setTempFileRoot(self, temp_root: str, storage_type: int): """设置临时文件根目录""" self._temp_root = temp_root def getMiddlewareVersion(self) -> int: """获取中间件版本""" return 0 def getSubscriptionId(self) -> int: return self._subscription_id def close(self): """关闭会话""" self._download_requests.clear() self._file_services.clear() @staticmethod def getDownloadStatus(context: Any, subscription_id: int, download_request: DownloadRequest) -> int: return DownloadRequest.DOWNLOAD_STATUS_INACTIVE def getFileServices(self) -> Dict[str, FileServiceInfo]: return dict(self._file_services) def getName(self) -> str: return "MbmsDownloadSession" def toString(self) -> str: return f"MbmsDownloadSession(subId={self._subscription_id})" # ═══════════════════════════════════════════════════════════════ # MbmsStreamingSession (pre-API 30) # ═══════════════════════════════════════════════════════════════ class MbmsStreamingSession(Stub): """MbmsStreamingSession — MBMS 流媒体会话 (旧版 API)""" EXTRA_MBMS_STREAMING_SERVICE = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE" EXTRA_MBMS_STREAMING_SERVICE_INFO = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE_INFO" _instance_map: Dict[int, MbmsStreamingSession] = {} def __init__(self, context: Any = None, subscription_id: int = 0, callback: Any = None): super().__init__() self._context = context self._subscription_id = subscription_id self._callback = callback self._middleware_bound = False self._streaming_services: Dict[str, StreamingService] = {} @staticmethod def create(context: Any, callback: Any, subscription_id: int = 0) -> MbmsStreamingSession: session = MbmsStreamingSession(context, subscription_id, callback) MbmsStreamingSession._instance_map[subscription_id] = session return session def startStreaming(self, service_info: FileServiceInfo, callback: StreamingServiceCallback) -> StreamingService: """开始流媒体""" service = StreamingService(service_info.getServiceId(), service_info.getServiceClass()) service.setCallback(callback) self._streaming_services[service_info.getServiceId()] = service return service def stopStreaming(self, streaming_service: StreamingService): """停止流媒体""" sid = streaming_service.getServiceId() if sid in self._streaming_services: streaming_service.stop() del self._streaming_services[sid] def requestUpdate(self, file_service_list: List[FileServiceInfo]): """请求更新服务列表""" pass def getCurrentStreamingServices(self) -> List[StreamingService]: """获取当前流媒体服务""" return list(self._streaming_services.values()) def getMiddlewareVersion(self) -> int: return 0 def getSubscriptionId(self) -> int: return self._subscription_id def close(self): """关闭会话""" for service in self._streaming_services.values(): service.close() self._streaming_services.clear() def getName(self) -> str: return "MbmsStreamingSession" def toString(self) -> str: return f"MbmsStreamingSession(subId={self._subscription_id})" # ═══════════════════════════════════════════════════════════════ # MbmsDownloadManager (API 30+) # ═══════════════════════════════════════════════════════════════ class MbmsDownloadManager(Stub): """MbmsDownloadManager — MBMS 下载管理器 (API 30+)""" EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.mbms.extra.MBMS_DOWNLOAD_RESULT" EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.mbms.extra.MBMS_DOWNLOAD_REQUEST" EXTRA_MBMS_FILE_INFO = "android.telephony.mbms.extra.MBMS_FILE_INFO" EXTRA_MBMS_COMPLETED_FILES = "android.telephony.mbms.extra.MBMS_COMPLETED_FILES" # 下载结果码 DOWNLOAD_RESULT_SUCCESS = 0 DOWNLOAD_RESULT_ERROR = 1 DOWNLOAD_RESULT_ERROR_MEDIA_NOT_FOUND = 2 DOWNLOAD_RESULT_ERROR_MEDIA_UNAVAILABLE = 3 DOWNLOAD_RESULT_ERROR_MEDIA_FAILED = 4 DOWNLOAD_RESULT_ERROR_SERVICE_UNAVAILABLE = 5 DOWNLOAD_RESULT_ERROR_INSUFFICIENT_STORAGE = 6 DOWNLOAD_RESULT_ERROR_IO_ERROR = 7 DOWNLOAD_RESULT_ERROR_FILE_NOT_FOUND = 8 DOWNLOAD_RESULT_ERROR_OUT_OF_MEMORY = 9 DOWNLOAD_RESULT_ERROR_RADIO_OFF = 10 DOWNLOAD_RESULT_ERROR_INTERRUPTED = 11 DOWNLOAD_RESULT_ERROR_TIMEOUT = 12 DOWNLOAD_RESULT_ERROR_CANCELLED = 13 DOWNLOAD_RESULT_ERROR_DOWNLOAD_NOT_STARTED = 14 def __init__(self, context: Any = None): super().__init__() self._context = context self._download_requests: Dict[int, DownloadRequest] = {} self._file_services: Dict[str, FileServiceInfo] = {} self._listeners: List[DownloadStatusListener] = [] self._progress_listeners: List[DownloadProgressListener] = [] @staticmethod def getService(context: Any, sub_id: int = 0) -> Optional[MbmsDownloadManager]: return MbmsDownloadManager(context) def addDownloadRequest(self, download_request: DownloadRequest): """添加下载请求""" request_id = len(self._download_requests) + 1 download_request.setRequestId(request_id) self._download_requests[request_id] = download_request def removeDownloadRequest(self, download_request: DownloadRequest): """移除下载请求""" rid = download_request.getRequestId() self._download_requests.pop(rid, None) def getCurrentDownloadRequests(self) -> List[DownloadRequest]: """获取当前所有下载请求""" return list(self._download_requests.values()) def getPendingDownloadRequests(self) -> List[DownloadRequest]: """获取待处理的下载请求""" return [r for r in self._download_requests.values() if r.getHash() == ""] def getCompletedDownloadRequests(self) -> List[DownloadRequest]: """获取已完成的下载请求""" return [r for r in self._download_requests.values() if r.getHash() != ""] def addStatusListener(self, listener: DownloadStatusListener): """添加状态监听器""" if listener not in self._listeners: self._listeners.append(listener) def removeStatusListener(self, listener: DownloadStatusListener): """移除状态监听器""" self._listeners = [l for l in self._listeners if l is not listener] def addProgressListener(self, listener: DownloadProgressListener): """添加进度监听器""" if listener not in self._progress_listeners: self._progress_listeners.append(listener) def removeProgressListener(self, listener: DownloadProgressListener): """移除进度监听器""" self._progress_listeners = [l for l in self._progress_listeners if l is not listener] def getMaxDownloadRequests(self) -> int: """获取最大下载请求数""" return 100 def getFileServices(self) -> Dict[str, FileServiceInfo]: return dict(self._file_services) def setTempFileRoot(self, temp_root: str, storage_type: int): """设置临时文件根目录""" pass def getTempFileRoot(self) -> str: return "" def getMiddlewareVersion(self) -> int: return 0 def close(self): """关闭下载管理器""" self._download_requests.clear() self._listeners.clear() self._progress_listeners.clear() def toString(self) -> str: return f"MbmsDownloadManager(requests={len(self._download_requests)})" # ═══════════════════════════════════════════════════════════════ # MbmsStreamingManager (API 30+) # ═══════════════════════════════════════════════════════════════ class MbmsStreamingManager(Stub): """MbmsStreamingManager — MBMS 流媒体管理器 (API 30+)""" EXTRA_MBMS_STREAMING_SERVICE_INFO = "android.telephony.mbms.extra.MBMS_STREAMING_SERVICE_INFO" STREAMING_SERVICE_STATE_STOPPED = 0 STREAMING_SERVICE_STATE_STARTED = 1 STREAMING_SERVICE_STATE_STALLED = 2 STREAMING_SERVICE_STATE_PENDING = 3 STREAMING_SERVICE_STATE_ERROR = 4 BROADCAST_METHOD_UNICAST = 0 BROADCAST_METHOD_MULTICAST = 1 BROADCAST_METHOD_BROADCAST = 2 REASON_BY_USER_REQUEST = 1 REASON_BY_MIDDLEWARE = 2 REASON_BY_RADIO_RESOURCE = 3 REASON_BY_OUT_OF_MEMORY = 4 REASON_BY_SIGNAL_LOST = 5 REASON_BY_TEMPORARILY_UNAVAILABLE = 6 REASON_BY_PERMANENTLY_UNAVAILABLE = 7 REASON_BY_SERVICE_SHUTDOWN = 8 REASON_BY_SERVICE_NOT_VISIBLE = 9 REASON_BY_RADIO_NOT_AVAILABLE = 10 def __init__(self, context: Any = None): super().__init__() self._context = context self._streaming_services: Dict[str, StreamingService] = {} self._service_info_map: Dict[str, FileServiceInfo] = {} @staticmethod def getService(context: Any, sub_id: int = 0) -> Optional[MbmsStreamingManager]: return MbmsStreamingManager(context) def startStreaming(self, service_info: FileServiceInfo, callback: StreamingServiceCallback) -> StreamingService: """启动流媒体服务""" service = StreamingService(service_info.getServiceId(), service_info.getServiceClass()) service.setCallback(callback) self._streaming_services[service_info.getServiceId()] = service self._service_info_map[service_info.getServiceId()] = service_info return service def stopStreaming(self, streaming_service: StreamingService): """停止流媒体服务""" sid = streaming_service.getServiceId() if sid in self._streaming_services: streaming_service.stop() del self._streaming_services[sid] def getCurrentStreamingServices(self) -> List[StreamingService]: return list(self._streaming_services.values()) def getServiceInfoForStreamingService(self, service: StreamingService) -> Optional[FileServiceInfo]: return self._service_info_map.get(service.getServiceId()) def getMaxStreamingServices(self) -> int: return 10 def getMiddlewareVersion(self) -> int: return 0 def close(self): """关闭流媒体管理器""" for service in self._streaming_services.values(): service.close() self._streaming_services.clear() self._service_info_map.clear() def toString(self) -> str: return f"MbmsStreamingManager(services={len(self._streaming_services)})" # ═══════════════════════════════════════════════════════════════ # MbmsUtils # ═══════════════════════════════════════════════════════════════ class MbmsUtils(Stub): """MbmsUtils — MBMS 工具类""" @staticmethod def getCellBroadcastServiceInfo(context: Any, service_id: str, subscription_id: int) -> Optional[FileServiceInfo]: """获取小区广播服务信息""" return None @staticmethod def isFileService(service_announcement: Any) -> bool: """判断是否为文件服务""" return True @staticmethod def isStreamingService(service_announcement: Any) -> bool: """判断是否为流媒体服务""" return True @staticmethod def getServiceClassFromServiceAnnouncement(service_announcement: Any) -> Optional[str]: """从服务公告中获取服务类""" return None @staticmethod def getServiceIdFromServiceAnnouncement(service_announcement: Any) -> Optional[str]: """从服务公告中获取服务 ID""" return None @staticmethod def getFileInfoFromFileService(service_info: FileServiceInfo) -> List[FileInfo]: """从文件服务信息中获取文件信息列表""" return service_info.getFiles() @staticmethod def createDownloadRequest(service_id: str, file_uri: str, source: Any, destination: Any) -> DownloadRequest: """创建下载请求""" return DownloadRequest(service_id, file_uri, source, destination) # ═══════════════════════════════════════════════════════════════ # MbmsGroupCallService # ═══════════════════════════════════════════════════════════════ class MbmsGroupCallService(Stub): """MbmsGroupCallService — MBMS 组呼服务""" REASON_BY_USER_REQUEST = 1 REASON_BY_MIDDLEWARE = 2 REASON_BY_RADIO_RESOURCE = 3 REASON_BY_OUT_OF_MEMORY = 4 REASON_BY_SIGNAL_LOST = 5 REASON_BY_TEMPORARILY_UNAVAILABLE = 6 REASON_BY_PERMANENTLY_UNAVAILABLE = 7 REASON_BY_SERVICE_SHUTDOWN = 8 REASON_BY_SERVICE_NOT_VISIBLE = 9 REASON_BY_RADIO_NOT_AVAILABLE = 10 STATE_STOPPED = 0 STATE_STARTED = 1 STATE_STALLED = 2 STATE_PENDING = 3 STATE_ERROR = 4 def __init__(self, context: Any = None): super().__init__() self._context = context self._group_calls: Dict[str, Any] = {} @staticmethod def getService(context: Any, sub_id: int = 0) -> Optional[MbmsGroupCallService]: return MbmsGroupCallService(context) def startGroupCall(self, service_id: str, sai_list: List[int], frequency: int, tmgi: Any, callback: Any): """启动组呼""" pass def stopGroupCall(self, service_id: str): """停止组呼""" self._group_calls.pop(service_id, None) def getCurrentGroupCalls(self) -> List[Any]: return list(self._group_calls.values()) def close(self): self._group_calls.clear() def toString(self) -> str: return f"MbmsGroupCallService(calls={len(self._group_calls)})" # ═══════════════════════════════════════════════════════════════ # MbmsDownloadSessionCallback # ═══════════════════════════════════════════════════════════════ class MbmsDownloadSessionCallback(Stub): """MbmsDownloadSessionCallback — MBMS 下载会话回调""" def onError(self, error_code: int, message: str = ""): pass def onFileServicesUpdated(self, services: List[FileServiceInfo]): pass def onMiddlewareReady(self): pass # ═══════════════════════════════════════════════════════════════ # MbmsStreamingSessionCallback # ═══════════════════════════════════════════════════════════════ class MbmsStreamingSessionCallback(Stub): """MbmsStreamingSessionCallback — MBMS 流媒体会话回调""" def onError(self, error_code: int, message: str = ""): pass def onStreamingServicesUpdated(self, services: List[FileServiceInfo]): pass def onMiddlewareReady(self): pass # ═══════════════════════════════════════════════════════════════ # ServiceAnnouncement # ═══════════════════════════════════════════════════════════════ class ServiceAnnouncement(Stub): """ServiceAnnouncement — MBMS 服务公告""" def __init__(self, service_id: str = "", service_class: str = "", announcement_data: bytes = b""): super().__init__() self._service_id = service_id self._service_class = service_class self._announcement_data = announcement_data self._sais: List[int] = [] self._cells: List[int] = [] self._expiry_time: int = 0 def getServiceId(self) -> str: return self._service_id def getServiceClass(self) -> str: return self._service_class def getAnnouncementData(self) -> bytes: return self._announcement_data def getSais(self) -> List[int]: return list(self._sais) def setSais(self, sais: List[int]): self._sais = list(sais) def getCells(self) -> List[int]: return list(self._cells) def setCells(self, cells: List[int]): self._cells = list(cells) def getExpiryTime(self) -> int: return self._expiry_time def setExpiryTime(self, expiry: int): self._expiry_time = expiry def toString(self) -> str: return f"ServiceAnnouncement(serviceId={self._service_id})" # ═══════════════════════════════════════════════════════════════ # InternalDownloadSession (helper) # ═══════════════════════════════════════════════════════════════ class InternalDownloadSession(Stub): """InternalDownloadSession — 内部下载会话辅助类""" def __init__(self): super().__init__() self._session_id: int = 0 self._active: bool = False self._downloads: Dict[int, DownloadRequest] = {} def getSessionId(self) -> int: return self._session_id def setSessionId(self, session_id: int): self._session_id = session_id def isActive(self) -> bool: return self._active def setActive(self, active: bool): self._active = active def addDownload(self, request: DownloadRequest): self._downloads[request.getRequestId()] = request def removeDownload(self, request_id: int): self._downloads.pop(request_id, None) def getDownloads(self) -> List[DownloadRequest]: return list(self._downloads.values()) def clear(self): self._downloads.clear() self._active = False def toString(self) -> str: return f"InternalDownloadSession(id={self._session_id}, active={self._active})" # ═══════════════════════════════════════════════════════════════ # MbmsResourceUtils # ═══════════════════════════════════════════════════════════════ class MbmsResourceUtils(Stub): """MbmsResourceUtils — MBMS 资源工具""" @staticmethod def getServiceIdList(services: List[FileServiceInfo]) -> List[str]: return [s.getServiceId() for s in services] @staticmethod def getServiceNameList(services: List[FileServiceInfo]) -> List[str]: return [s.getServiceClass() for s in services] @staticmethod def findServiceById(services: List[FileServiceInfo], service_id: str) -> Optional[FileServiceInfo]: for s in services: if s.getServiceId() == service_id: return s return None @staticmethod def mergeServiceLists(a: List[FileServiceInfo], b: List[FileServiceInfo]) -> List[FileServiceInfo]: merged = {s.getServiceId(): s for s in a} for s in b: merged[s.getServiceId()] = s return list(merged.values())