- 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关键字冲突
1090 lines
37 KiB
Python
1090 lines
37 KiB
Python
"""
|
|
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})" |