""" Android DRM 组件 — DrmManager, DrmStore, DrmInfo, DrmManagerClient, DrmOutputStream 等 方法签名完全对齐 Android SDK (android.drm 包) """ 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 # ═══════════════════════════════════════════════════════════════ # DrmStore # ═══════════════════════════════════════════════════════════════ class DrmStore(Stub): """DrmStore — DRM 存储常量""" # ═══════════════════════════════════════════════════ # DrmStore.Action # ═══════════════════════════════════════════════════ class Action(Stub): """DrmStore.Action — DRM 操作类型""" PLAY = 1 RINGTONE = 2 TRANSFER = 3 OUTPUT = 4 PREVIEW = 5 EXECUTE = 6 DISPLAY = 7 PRINT = 8 WALLPAPER = 9 RINGTONEOUTGOING = 10 RINGTONECALL = 11 RINGTONESILENT = 12 RINGTONESILENTCALL = 13 RINGTONESILENTOUTGOING = 14 RINGTONESILENTALARM = 15 ALARM = 16 NOTIFICATION = 17 CALENDAR = 18 CLOCK = 19 MAX = 20 @staticmethod def toString(action: int) -> str: names = { DrmStore.Action.PLAY: "PLAY", DrmStore.Action.RINGTONE: "RINGTONE", DrmStore.Action.TRANSFER: "TRANSFER", DrmStore.Action.OUTPUT: "OUTPUT", DrmStore.Action.PREVIEW: "PREVIEW", DrmStore.Action.EXECUTE: "EXECUTE", DrmStore.Action.DISPLAY: "DISPLAY", DrmStore.Action.PRINT: "PRINT", DrmStore.Action.WALLPAPER: "WALLPAPER", DrmStore.Action.ALARM: "ALARM", DrmStore.Action.NOTIFICATION: "NOTIFICATION", } return names.get(action, f"UNKNOWN({action})") class ActionConstant(Stub): PLAY = 1 RINGTONE = 2 TRANSFER = 3 OUTPUT = 4 PREVIEW = 5 EXECUTE = 6 DISPLAY = 7 PRINT = 8 WALLPAPER = 9 ALARM = 10 NOTIFICATION = 11 class ActionDescription(Stub): PLAY_DESCRIPTION = "play" RINGTONE_DESCRIPTION = "ringtone" TRANSFER_DESCRIPTION = "transfer" OUTPUT_DESCRIPTION = "output" PREVIEW_DESCRIPTION = "preview" EXECUTE_DESCRIPTION = "execute" DISPLAY_DESCRIPTION = "display" PRINT_DESCRIPTION = "print" WALLPAPER_DESCRIPTION = "wallpaper" ALARM_DESCRIPTION = "alarm" NOTIFICATION_DESCRIPTION = "notification" # ═══════════════════════════════════════════════════ # DrmStore.ConstraintsColumns # ═══════════════════════════════════════════════════ class ConstraintsColumns(Stub): """DrmStore.ConstraintsColumns — DRM 约束列""" MAX_REPEAT_COUNT = "max_repeat_count" REMAINING_REPEAT_COUNT = "remaining_repeat_count" LICENSE_START_TIME = "license_start_time" LICENSE_EXPIRY_TIME = "license_expiry_time" AVAILABLE_PERIOD = "available_period" EXTENDED_METADATA = "extended_metadata" LICENSE_AVAILABLE_TIME = "license_available_time" LICENSE_AVAILABLE_PERIOD = "license_available_period" LICENSE_USE_COUNT = "license_use_count" LICENSE_USE_LIMIT = "license_use_limit" LICENSE_RIGHTS = "license_rights" LICENSE_RIGHTS_INFO = "license_rights_info" LICENSE_CATEGORY = "license_category" LICENSE_VERSION = "license_version" # ═══════════════════════════════════════════════════ # DrmStore.DrmObjectType # ═══════════════════════════════════════════════════ class DrmObjectType(Stub): """DrmStore.DrmObjectType — DRM 对象类型""" UNKNOWN = 0 CONTENT = 1 RIGHTS_OBJECT = 2 TRIGGER_OBJECT = 3 # ═══════════════════════════════════════════════════ # DrmStore.PlaybackCtrl # ═══════════════════════════════════════════════════ class PlaybackCtrl(Stub): """DrmStore.PlaybackCtrl — 播放控制""" START = 0 STOP = 1 PAUSE = 2 RESUME = 3 # ═══════════════════════════════════════════════════ # DrmStore.RightsStatus # ═══════════════════════════════════════════════════ class RightsStatus(Stub): """DrmStore.RightsStatus — 权限状态""" RIGHTS_VALID = 0 RIGHTS_INVALID = 1 RIGHTS_EXPIRED = 2 RIGHTS_NOT_ACQUIRED = 3 RIGHTS_PROTECTED = 4 RIGHTS_UNPROTECTED = 5 RIGHTS_NOT_INSTALLED = 6 RIGHTS_INSTALLED = 7 RIGHTS_META = 8 RIGHTS_COPYRIGHT = 9 RIGHTS_COPYRIGHT_EXPIRED = 10 RIGHTS_NOT_COPYRIGHT = 11 RIGHTS_SECURE = 12 RIGHTS_NOT_SECURE = 13 RIGHTS_UNLIMITED = 14 RIGHTS_LIMITED = 15 # ═══════════════════════════════════════════════════ # DrmStore.DrmMethod # ═══════════════════════════════════════════════════ class DrmMethod(Stub): """DrmStore.DrmMethod — DRM 方法""" METHOD_UNKNOWN = 0 METHOD_FL = 1 METHOD_NONE = 2 METHOD_FLD = 3 METHOD_SD = 4 METHOD_SD_PRELOAD = 5 METHOD_SD_DOWNLOAD = 6 METHOD_FLD_FOREGROUND = 7 METHOD_FLD_BACKGROUND = 8 # ═══════════════════════════════════════════════════ # DrmStore.DrmStatus # ═══════════════════════════════════════════════════ class DrmStatus(Stub): """DrmStore.DrmStatus — DRM 状态""" STATUS_OK = 0 STATUS_ERROR = 1 STATUS_WAITING = 2 STATUS_CANCELLED = 3 STATUS_REJECTED = 4 STATUS_NOT_SUPPORTED = 5 STATUS_NO_RIGHTS = 6 STATUS_NO_PERMISSION = 7 STATUS_PENDING = 8 STATUS_PROCESSING = 9 STATUS_COMPLETED = 10 STATUS_FAILED = 11 STATUS_INTERRUPTED = 12 STATUS_TIMEOUT = 13 STATUS_NO_MEMORY = 14 STATUS_SERVICE_NOT_FOUND = 15 STATUS_SERVICE_NOT_AVAILABLE = 16 STATUS_INVALID_ARGUMENT = 17 STATUS_ILLEGAL_STATE = 18 STATUS_NETWORK_ERROR = 19 STATUS_STORAGE_ERROR = 20 STATUS_SECURITY_ERROR = 21 STATUS_LICENSE_EXPIRED = 22 STATUS_LICENSE_NOT_FOUND = 23 STATUS_LICENSE_NOT_AVAILABLE = 24 DRM_METHOD_FL = 1 DRM_METHOD_NONE = 2 DRM_METHOD_UNKNOWN = 0 EXTENSION_NONE = "none" EXTENSION_FL = ".fl" EXTENSION_DCF = ".dcf" EXTENSION_DM = ".dm" EXTENSION_ODF = ".odf" EXTENSION_SDCF = ".sdcf" EXTENSION_FLD = ".fld" # ═══════════════════════════════════════════════════════════════ # DrmInfo # ═══════════════════════════════════════════════════════════════ class DrmInfo(Stub): """DrmInfo — DRM 信息""" def __init__(self, mime_type: str = "", data: bytes = b""): super().__init__() self._mime_type = mime_type self._data = data self._attributes: Dict[str, str] = {} self._info_type: int = 0 self._content_type: str = "" self._content_uri: str = "" self._content_data: bytes = b"" self._is_valid: bool = True self._metadata: Dict[str, str] = {} def getMimeType(self) -> str: return self._mime_type def setMimeType(self, mime_type: str): self._mime_type = mime_type def getData(self) -> bytes: return self._data def setData(self, data: bytes): self._data = data def get(self, key: str) -> Optional[str]: return self._attributes.get(key) def put(self, key: str, value: str): self._attributes[key] = value def getAttributes(self) -> Dict[str, str]: return dict(self._attributes) def getInfoType(self) -> int: return self._info_type def setInfoType(self, type_: int): self._info_type = type_ def getContentType(self) -> str: return self._content_type def setContentType(self, type_: str): self._content_type = type_ def getContentUri(self) -> str: return self._content_uri def setContentUri(self, uri: str): self._content_uri = uri def getContentData(self) -> bytes: return self._content_data def setContentData(self, data: bytes): self._content_data = data def isValid(self) -> bool: return self._is_valid def setValid(self, valid: bool): self._is_valid = valid def getMetadata(self) -> Dict[str, str]: return dict(self._metadata) def setMetadata(self, metadata: Dict[str, str]): self._metadata = dict(metadata) def toString(self) -> str: return f"DrmInfo(mime={self._mime_type}, type={self._info_type})" # ═══════════════════════════════════════════════════════════════ # DrmInfoRequest # ═══════════════════════════════════════════════════════════════ class DrmInfoRequest(Stub): """DrmInfoRequest — DRM 信息请求""" TYPE_REGISTRATION_INFO = 1 TYPE_UNREGISTRATION_INFO = 2 TYPE_RIGHTS_ACQUISITION_INFO = 3 TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4 TYPE_MERGE_INFO = 5 TYPE_REMOVE_INFO = 6 TYPE_ACCOUNT_INFO = 7 TYPE_ACCOUNT_REGISTRATION_INFO = 8 TYPE_ACCOUNT_UNREGISTRATION_INFO = 9 TYPE_ACCOUNT_UPDATE_INFO = 10 TYPE_ADAPTER_INFO = 11 TYPE_ADAPTER_CONFIG_INFO = 12 TYPE_ADAPTER_REGISTRATION_INFO = 13 TYPE_METADATA_INFO = 14 def __init__(self, info_type: int = TYPE_REGISTRATION_INFO, mime_type: str = ""): super().__init__() self._info_type = info_type self._mime_type = mime_type self._attributes: Dict[str, str] = {} def getInfoType(self) -> int: return self._info_type def getMimeType(self) -> str: return self._mime_type def get(self, key: str) -> Optional[str]: return self._attributes.get(key) def put(self, key: str, value: str): self._attributes[key] = value def getAttributes(self) -> Dict[str, str]: return dict(self._attributes) def toString(self) -> str: return f"DrmInfoRequest(type={self._info_type}, mime={self._mime_type})" # ═══════════════════════════════════════════════════════════════ # DrmInfoResponse # ═══════════════════════════════════════════════════════════════ class DrmInfoResponse(Stub): """DrmInfoResponse — DRM 信息响应""" def __init__(self): super().__init__() self._info: Optional[DrmInfo] = None self._status: int = 0 self._error_message: str = "" self._request: Optional[DrmInfoRequest] = None self._response_data: bytes = b"" self._timestamp: int = 0 def getInfo(self) -> Optional[DrmInfo]: return self._info def setInfo(self, info: DrmInfo): self._info = info def getStatus(self) -> int: return self._status def setStatus(self, status: int): self._status = status def getErrorMessage(self) -> str: return self._error_message def setErrorMessage(self, msg: str): self._error_message = msg def getRequest(self) -> Optional[DrmInfoRequest]: return self._request def setRequest(self, request: DrmInfoRequest): self._request = request def getResponseData(self) -> bytes: return self._response_data def setResponseData(self, data: bytes): self._response_data = data def getTimestamp(self) -> int: return self._timestamp def setTimestamp(self, ts: int): self._timestamp = ts def isSuccessful(self) -> bool: return self._status == DrmStore.DrmStatus.STATUS_OK def toString(self) -> str: return f"DrmInfoResponse(status={self._status})" # ═══════════════════════════════════════════════════════════════ # DrmConvertedStatus # ═══════════════════════════════════════════════════════════════ class DrmConvertedStatus(Stub): """DrmConvertedStatus — DRM 转换状态""" STATUS_OK = 0 STATUS_ERROR = 1 STATUS_INPUT_DATA_ERROR = 2 STATUS_OPERATION_NOT_SUPPORTED = 3 STATUS_INTERNAL_ERROR = 4 STATUS_INVALID_DATA = 5 STATUS_NO_RIGHTS = 6 STATUS_ENCRYPTION_ERROR = 7 def __init__(self, status_code: int = STATUS_OK, converted_data: bytes = b"", mime_type: str = ""): super().__init__() self._status_code = status_code self._converted_data = converted_data self._mime_type = mime_type self._converted_file_path: str = "" self._original_file_path: str = "" self._conversion_time: int = 0 def getStatusCode(self) -> int: return self._status_code def setStatusCode(self, code: int): self._status_code = code def getConvertedData(self) -> bytes: return self._converted_data def setConvertedData(self, data: bytes): self._converted_data = data def getMimeType(self) -> str: return self._mime_type def setMimeType(self, mime_type: str): self._mime_type = mime_type def getConvertedFilePath(self) -> str: return self._converted_file_path def setConvertedFilePath(self, path: str): self._converted_file_path = path def getOriginalFilePath(self) -> str: return self._original_file_path def setOriginalFilePath(self, path: str): self._original_file_path = path def isSuccessful(self) -> bool: return self._status_code == DrmConvertedStatus.STATUS_OK def toString(self) -> str: return f"DrmConvertedStatus(status={self._status_code})" # ═══════════════════════════════════════════════════════════════ # DrmEvent # ═══════════════════════════════════════════════════════════════ class DrmEvent(Stub): """DrmEvent — DRM 事件""" TYPE_ALL_RIGHTS_REMOVED = 1001 TYPE_DRM_INFO_PROCESSED = 1002 TYPE_RIGHTS_INSTALLED = 1003 TYPE_RIGHTS_REMOVED = 1004 TYPE_RIGHTS_EXPIRED = 1005 TYPE_RIGHTS_ACQUISITION_STARTED = 1006 TYPE_RIGHTS_ACQUISITION_FINISHED = 1007 TYPE_RIGHTS_ACQUISITION_PROGRESS = 1008 TYPE_RIGHTS_ACQUISITION_FAILED = 1009 TYPE_META_DATA_AVAILABLE = 1010 TYPE_META_DATA_UPDATED = 1011 TYPE_META_DATA_REMOVED = 1012 TYPE_ACCOUNT_ALREADY_REGISTERED = 1013 TYPE_ACCOUNT_REGISTERED = 1014 TYPE_ACCOUNT_UNREGISTERED = 1015 def __init__(self, unique_id: int = 0, type_: int = 0, message: str = ""): super().__init__() self._unique_id = unique_id self._type = type_ self._message = message self._attributes: Dict[str, Any] = {} self._timestamp: int = int(time.time() * 1000) def getUniqueId(self) -> int: return self._unique_id def getType(self) -> int: return self._type def getMessage(self) -> str: return self._message def getTimestamp(self) -> int: return self._timestamp def getAttribute(self, key: str) -> Optional[Any]: return self._attributes.get(key) def getAttributes(self) -> Dict[str, Any]: return dict(self._attributes) def setAttribute(self, key: str, value: Any): self._attributes[key] = value def toString(self) -> str: return f"DrmEvent(type={self._type}, msg={self._message})" # ═══════════════════════════════════════════════════════════════ # DrmErrorEvent # ═══════════════════════════════════════════════════════════════ class DrmErrorEvent(DrmEvent): """DrmErrorEvent — DRM 错误事件""" TYPE_RIGHTS_NOT_INSTALLED = 2001 TYPE_RIGHTS_ACQUISITION_FAILED = 2002 TYPE_RIGHTS_NOT_ACQUIRED = 2003 TYPE_PROCESS_DRM_INFO_FAILED = 2004 TYPE_REMOVE_ALL_RIGHTS_FAILED = 2005 TYPE_ACQUIRE_RIGHTS_FAILED = 2006 TYPE_SIGNATURE_MISMATCH = 2007 TYPE_INVALID_DRM_INFO = 2008 TYPE_CERTIFICATE_REVOKED = 2009 TYPE_LICENSE_EXPIRED = 2010 TYPE_LICENSE_NOT_FOUND = 2011 TYPE_SECURITY_VIOLATION = 2012 TYPE_NETWORK_ERROR = 2013 def __init__(self, unique_id: int = 0, type_: int = 0, message: str = ""): super().__init__(unique_id, type_, message) self._error_code: int = 0 self._recoverable: bool = False self._stack_trace: str = "" def getErrorCode(self) -> int: return self._error_code def setErrorCode(self, code: int): self._error_code = code def isRecoverable(self) -> bool: return self._recoverable def setRecoverable(self, recoverable: bool): self._recoverable = recoverable def getStackTrace(self) -> str: return self._stack_trace def setStackTrace(self, trace: str): self._stack_trace = trace # ═══════════════════════════════════════════════════════════════ # DrmInfoEvent # ═══════════════════════════════════════════════════════════════ class DrmInfoEvent(DrmEvent): """DrmInfoEvent — DRM 信息事件""" TYPE_ACCOUNT_ALREADY_REGISTERED = 3001 TYPE_ACCOUNT_REGISTERED = 3002 TYPE_ACCOUNT_UNREGISTERED = 3003 TYPE_ACCOUNT_REGISTRATION_COMPLETED = 3004 TYPE_ACCOUNT_REGISTRATION_FAILED = 3005 TYPE_ACCOUNT_UNREGISTRATION_COMPLETED = 3006 TYPE_ACCOUNT_UNREGISTRATION_FAILED = 3007 TYPE_ADAPTER_REGISTRATION_COMPLETED = 3008 TYPE_ADAPTER_REGISTRATION_FAILED = 3009 TYPE_METADATA_RETRIEVED = 3010 TYPE_METADATA_RETRIEVAL_FAILED = 3011 TYPE_RIGHTS_ACQUISITION_PROGRESS = 3012 TYPE_RIGHTS_ACQUISITION_COMPLETED = 3013 TYPE_RIGHTS_ACQUISITION_FAILED = 3014 def __init__(self, unique_id: int = 0, type_: int = 0, message: str = ""): super().__init__(unique_id, type_, message) self._data: Any = None self._info_request: Optional[DrmInfoRequest] = None def getData(self) -> Any: return self._data def setData(self, data: Any): self._data = data def getInfoRequest(self) -> Optional[DrmInfoRequest]: return self._info_request def setInfoRequest(self, request: DrmInfoRequest): self._info_request = request # ═══════════════════════════════════════════════════════════════ # DrmManagerClient # ═══════════════════════════════════════════════════════════════ class DrmManagerClient(Stub): """DrmManagerClient — DRM 管理器客户端""" ERROR_NONE = 0 ERROR_UNKNOWN = -1 ERROR_NOT_INITIALIZED = -2 ERROR_NOT_SUPPORTED = -3 ERROR_INVALID_ARGUMENT = -4 ERROR_PERMISSION_DENIED = -5 ERROR_SERVICE_NOT_FOUND = -6 ERROR_SERVICE_NOT_AVAILABLE = -7 ERROR_NETWORK = -8 ERROR_STORAGE = -9 ERROR_IO = -10 ERROR_INVALID_DRM_INFO = -11 ERROR_RIGHTS_NOT_ACQUIRED = -12 ERROR_RIGHTS_EXPIRED = -13 ERROR_NO_RIGHTS = -14 ERROR_INTERNAL = -99 class OnErrorListener(Stub): """DrmManagerClient.OnErrorListener — 错误监听器""" def onError(self, client: DrmManagerClient, event: DrmErrorEvent): pass class OnInfoListener(Stub): """DrmManagerClient.OnInfoListener — 信息监听器""" def onInfo(self, client: DrmManagerClient, event: DrmInfoEvent): pass class OnEventListener(Stub): """DrmManagerClient.OnEventListener — 事件监听器""" def onEvent(self, client: DrmManagerClient, event: DrmEvent): pass def __init__(self, context: Any = None): super().__init__() self._context = context self._unique_id: int = id(self) self._is_initialized: bool = True self._error_listeners: List[DrmManagerClient.OnErrorListener] = [] self._info_listeners: List[DrmManagerClient.OnInfoListener] = [] self._event_listeners: List[DrmManagerClient.OnEventListener] = [] def acquireRights(self, drm_info_request: DrmInfoRequest) -> DrmInfoResponse: """获取权限""" response = DrmInfoResponse() response.setRequest(drm_info_request) response.setStatus(DrmStore.DrmStatus.STATUS_OK) return response def processDrmInfo(self, drm_info: DrmInfo) -> DrmInfoResponse: """处理 DRM 信息""" response = DrmInfoResponse() response.setInfo(drm_info) response.setStatus(DrmStore.DrmStatus.STATUS_OK) return response def getDrmInfo(self, drm_info_request: DrmInfoRequest) -> Optional[DrmInfo]: """获取 DRM 信息""" drm_info = DrmInfo(drm_info_request.getMimeType()) drm_info.setInfoType(drm_info_request.getInfoType()) return drm_info def getConstraints(self, path: str, action: int) -> Dict[str, Any]: """获取约束""" return {} def getMetadata(self, path: str) -> Dict[str, str]: """获取元数据""" return {} def canHandle(self, path: str, mime_type: str) -> bool: """检查能否处理""" return True def removeAllListener(self): """移除所有监听器""" self._error_listeners.clear() self._info_listeners.clear() self._event_listeners.clear() def removeListener(self, listener: Any): """移除监听器""" if listener in self._error_listeners: self._error_listeners.remove(listener) if listener in self._info_listeners: self._info_listeners.remove(listener) if listener in self._event_listeners: self._event_listeners.remove(listener) def setOnErrorListener(self, listener: OnErrorListener): self._error_listeners.append(listener) def setOnInfoListener(self, listener: OnInfoListener): self._info_listeners.append(listener) def setOnEventListener(self, listener: OnEventListener): self._event_listeners.append(listener) def getUniqueId(self) -> int: return self._unique_id def release(self): """释放客户端""" self._is_initialized = False self.removeAllListener() def isInitialized(self) -> bool: return self._is_initialized def toString(self) -> str: return f"DrmManagerClient(id={self._unique_id})" # ═══════════════════════════════════════════════════════════════ # DrmOutputStream # ═══════════════════════════════════════════════════════════════ class DrmOutputStream(Stub): """DrmOutputStream — DRM 输出流""" def __init__(self, file_path: str = ""): super().__init__() self._file_path = file_path self._is_open: bool = False self._is_finished: bool = False self._buffer: bytes = b"" self._bytes_written: int = 0 self._mime_type: str = "" self._drm_info: Optional[DrmInfo] = None def open(self) -> bool: """打开输出流""" self._is_open = True return True def write(self, data: bytes) -> int: """写入数据""" if not self._is_open: return -1 self._buffer += data self._bytes_written += len(data) return len(data) def close(self): """关闭输出流""" if self._is_open: self._is_open = False self._is_finished = True def finish(self): """完成写入""" self._is_finished = True def getFilePath(self) -> str: return self._file_path def setFilePath(self, path: str): self._file_path = path def isOpen(self) -> bool: return self._is_open def isFinished(self) -> bool: return self._is_finished def getBytesWritten(self) -> int: return self._bytes_written def getBuffer(self) -> bytes: return self._buffer def getMimeType(self) -> str: return self._mime_type def setMimeType(self, mime_type: str): self._mime_type = mime_type def toString(self) -> str: return f"DrmOutputStream(path={self._file_path}, written={self._bytes_written})" # ═══════════════════════════════════════════════════════════════ # DrmRights # ═══════════════════════════════════════════════════════════════ class DrmRights(Stub): """DrmRights — DRM 权限""" def __init__(self, rights_data: bytes = b"", mime_type: str = "", account_id: str = "", subscription_id: str = ""): super().__init__() self._rights_data = rights_data self._mime_type = mime_type self._account_id = account_id self._subscription_id = subscription_id self._is_valid: bool = True self._expiry_time: int = 0 self._issued_time: int = int(time.time() * 1000) def getData(self) -> bytes: return self._rights_data def setData(self, data: bytes): self._rights_data = data def getMimeType(self) -> str: return self._mime_type def setMimeType(self, mime_type: str): self._mime_type = mime_type def getAccountId(self) -> str: return self._account_id def setAccountId(self, account_id: str): self._account_id = account_id def getSubscriptionId(self) -> str: return self._subscription_id def setSubscriptionId(self, sub_id: str): self._subscription_id = sub_id def isValid(self) -> bool: return self._is_valid and (self._expiry_time == 0 or self._expiry_time > int(time.time() * 1000)) def setValid(self, valid: bool): self._is_valid = valid def getExpiryTime(self) -> int: return self._expiry_time def setExpiryTime(self, expiry_ms: int): self._expiry_time = expiry_ms def getIssuedTime(self) -> int: return self._issued_time def isExpired(self) -> bool: if self._expiry_time == 0: return False return self._expiry_time < int(time.time() * 1000) def toString(self) -> str: return f"DrmRights(mime={self._mime_type}, valid={self._is_valid})" # ═══════════════════════════════════════════════════════════════ # DrmSupportInfo # ═══════════════════════════════════════════════════════════════ class DrmSupportInfo(Stub): """DrmSupportInfo — DRM 支持信息""" def __init__(self): super().__init__() self._mime_types: Set[str] = set() self._file_suffixes: Set[str] = set() self._descriptions: List[str] = [] self._plug_in_id: str = "" self._vendor_name: str = "" self._plugin_version: str = "" self._is_available: bool = True def addMimeType(self, mime_type: str): self._mime_types.add(mime_type) def addFileSuffix(self, file_suffix: str): self._file_suffixes.add(file_suffix) def addDescription(self, description: str): self._descriptions.append(description) def getMimeTypes(self) -> Set[str]: return set(self._mime_types) def getFileSuffixes(self) -> Set[str]: return set(self._file_suffixes) def getDescriptions(self) -> List[str]: return list(self._descriptions) def isSupportedMimeType(self, mime_type: str) -> bool: return mime_type in self._mime_types def isSupportedFileSuffix(self, suffix: str) -> bool: return suffix in self._file_suffixes def getPlugInId(self) -> str: return self._plug_in_id def setPlugInId(self, plugin_id: str): self._plug_in_id = plugin_id def getVendorName(self) -> str: return self._vendor_name def setVendorName(self, name: str): self._vendor_name = name def getPluginVersion(self) -> str: return self._plugin_version def setPluginVersion(self, version: str): self._plugin_version = version def isAvailable(self) -> bool: return self._is_available def setAvailable(self, available: bool): self._is_available = available def toString(self) -> str: return f"DrmSupportInfo(mimeTypes={len(self._mime_types)}, suffixes={len(self._file_suffixes)})" # ═══════════════════════════════════════════════════════════════ # DrmUtils # ═══════════════════════════════════════════════════════════════ class DrmUtils(Stub): """DrmUtils — DRM 工具类""" EXTENSION_DCF = ".dcf" EXTENSION_FL = ".fl" EXTENSION_DM = ".dm" EXTENSION_ODF = ".odf" EXTENSION_SDCF = ".sdcf" EXTENSION_FLD = ".fld" EXTENSION_HTML = ".html" EXTENSION_XHTML = ".xhtml" EXTENSION_XML = ".xml" MIME_TYPE_DCF = "application/vnd.oma.drm.content" MIME_TYPE_FL = "application/vnd.oma.drm.message" MIME_TYPE_DM = "application/vnd.oma.drm.message" MIME_TYPE_ODF = "application/vnd.oma.drm.content" MIME_TYPE_SDCF = "application/vnd.oma.drm.content" MIME_TYPE_UNKNOWN = "application/octet-stream" @staticmethod def getOriginalMimeType(context: Any, file_path: str) -> str: """获取原始 MIME 类型""" return DrmUtils.MIME_TYPE_UNKNOWN @staticmethod def getOriginalFileSuffix(context: Any, file_path: str) -> str: """获取原始文件后缀""" _, ext = os.path.splitext(file_path) if hasattr(os.path, 'splitext') else (file_path, "") return ext @staticmethod def isDrmFile(file_path: str) -> bool: """检查是否为 DRM 文件""" if not file_path: return False suffix = os.path.splitext(file_path)[1].lower() if hasattr(os.path, 'splitext') else "" drm_extensions = {DrmUtils.EXTENSION_DCF, DrmUtils.EXTENSION_FL, DrmUtils.EXTENSION_DM, DrmUtils.EXTENSION_ODF, DrmUtils.EXTENSION_SDCF, DrmUtils.EXTENSION_FLD} return suffix in drm_extensions @staticmethod def isDrmMimeType(mime_type: str) -> bool: """检查是否为 DRM MIME 类型""" drm_mime_types = {DrmUtils.MIME_TYPE_DCF, DrmUtils.MIME_TYPE_FL, DrmUtils.MIME_TYPE_DM, DrmUtils.MIME_TYPE_ODF, DrmUtils.MIME_TYPE_SDCF} return mime_type in drm_mime_types @staticmethod def getDrmFileType(file_path: str) -> int: """获取 DRM 文件类型""" if not DrmUtils.isDrmFile(file_path): return DrmStore.DrmObjectType.UNKNOWN return DrmStore.DrmObjectType.CONTENT @staticmethod def getSupportedMimeTypes() -> Set[str]: """获取支持的 MIME 类型""" return {DrmUtils.MIME_TYPE_DCF, DrmUtils.MIME_TYPE_FL, DrmUtils.MIME_TYPE_DM, DrmUtils.MIME_TYPE_ODF, DrmUtils.MIME_TYPE_SDCF} @staticmethod def convertToDrmFile(input_path: str, output_path: str, mime_type: str) -> DrmConvertedStatus: """转换为 DRM 文件""" with open(input_path, "rb") as f: data = f.read() status = DrmConvertedStatus(DrmConvertedStatus.STATUS_OK, data, mime_type) status.setOriginalFilePath(input_path) status.setConvertedFilePath(output_path) return status # ═══════════════════════════════════════════════════════════════ # DrmManager (高层管理器) # ═══════════════════════════════════════════════════════════════ class DrmManager(Stub): """DrmManager — DRM 管理器""" def __init__(self, context: Any = None): super().__init__() self._context = context self._clients: Dict[int, DrmManagerClient] = {} self._is_available: bool = True self._support_infos: List[DrmSupportInfo] = [] @staticmethod def getInstance(context: Any) -> DrmManager: return DrmManager(context) def getDrmManagerClient(self) -> DrmManagerClient: client = DrmManagerClient(self._context) client_id = client.getUniqueId() self._clients[client_id] = client return client def releaseDrmManagerClient(self, client: DrmManagerClient): client_id = client.getUniqueId() if client_id in self._clients: client.release() del self._clients[client_id] def isAvailable(self) -> bool: return self._is_available def setAvailable(self, available: bool): self._is_available = available def getSupportInfos(self) -> List[DrmSupportInfo]: return list(self._support_infos) def addSupportInfo(self, info: DrmSupportInfo): self._support_infos.append(info) def removeSupportInfo(self, info: DrmSupportInfo): self._support_infos = [i for i in self._support_infos if i is not info] def canHandle(self, path: str, mime_type: str) -> bool: for client in self._clients.values(): if client.canHandle(path, mime_type): return True return False def getAvailableClients(self) -> List[DrmManagerClient]: return [c for c in self._clients.values() if c.isInitialized()] def release(self): for client in list(self._clients.values()): client.release() self._clients.clear() self._support_infos.clear() def toString(self) -> str: return f"DrmManager(clients={len(self._clients)}, available={self._is_available})"