feat: 新增 42 个 Android API 模块

新增模块覆盖:
系统层 (14): system, security, print, opengl, renderscript, transition,
test, service_notification, service_controls, service_quicksettings,
service_wallpaper, service_autofill, service_dreams, service_vr

应用层 (13): app_admin, app_backup, app_job, app_usage, app_role,
app_people, appwidget, companion, credentials, mtp, sip, telecom, speech

硬件媒体层 (15): hardware_display, hardware_fingerprint, hardware_usb,
hardware_input, media_audiofx, media_browse, media_session, media_tv,
media_midi, media_projection, uwb, se, view_autofill, view_textclassifier,
view_translation

总计 94 个模块,75,342 行代码
This commit is contained in:
shuiyu1123 committed 2026-06-24 12:00:28 +00:00
1 parent 8ad62be068
commit 506f9ac725
43 files changed
+16704 -23

No files matched your search

+477
View File
@@ -0,0 +1,477 @@
"""
Android Backup API — BackupManager, BackupAgent, BackupDataInput/Output, RestoreObserver 等
方法签名完全对齐 Android SDK
"""
import json
import os as _os
import sys
import tempfile
import shutil
import time
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple, Callable, BinaryIO
from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir
from ..java.lang import RuntimeException
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
__all__ = [
"BackupManager",
"BackupAgent",
"BackupDataInput",
"BackupDataOutput",
"FullBackupDataOutput",
"BackupHelper",
"FileBackupHelper",
"SharedPreferencesBackupHelper",
"RestoreObserver",
"BackupDataInputStream",
]
# ═══════════════════════════════════════════════════════════════
# 备份存储路径
# ═══════════════════════════════════════════════════════════════
_BACKUP_DIR = _os.path.join(_app_data_dir, "backups")
def _ensure_backup_dir():
_os.makedirs(_BACKUP_DIR, exist_ok=True)
_ensure_backup_dir()
# ═══════════════════════════════════════════════════════════════
# BackupManager
# ═══════════════════════════════════════════════════════════════
class BackupManager(Stub):
"""BackupManager — 备份管理器 (桌面文件备份模拟)"""
def __init__(self, context: Any = None):
super().__init__()
self._context = context
self._backup_enabled = True
def dataChanged(self, packageName: str = None):
"""通知系统数据已更改,需要备份"""
if packageName:
flag_path = _os.path.join(_BACKUP_DIR, f"{packageName}.pending")
elif self._context:
flag_path = _os.path.join(_BACKUP_DIR, f"{self._context.getPackageName()}.pending")
else:
flag_path = _os.path.join(_BACKUP_DIR, "default.pending")
with open(flag_path, "w") as f:
f.write(time.time())
def requestRestore(self, observer: "RestoreObserver") -> int:
"""请求恢复备份数据"""
raise AndroidOnlyError("BackupManager.requestRestore() 仅在 Android 设备上可用")
def getCurrentTransport(self) -> Optional[str]:
"""获取当前备份传输名称"""
return "com.android.localtransport.LocalTransport"
def listAllTransports(self) -> List[str]:
"""列出所有可用备份传输"""
return ["com.android.localtransport.LocalTransport"]
def isBackupEnabled(self) -> bool:
"""检查备份是否启用"""
return self._backup_enabled
def setBackupEnabled(self, isEnabled: bool):
"""设置备份启用状态"""
self._backup_enabled = isEnabled
def toString(self) -> str:
return f"BackupManager(backup_enabled={self._backup_enabled})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupAgent
# ═══════════════════════════════════════════════════════════════
class BackupAgent(Stub):
"""BackupAgent — 备份代理基类"""
def __init__(self):
super().__init__()
def onCreate(self):
"""创建备份代理时调用"""
pass
def onBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行备份操作"""
pass
def onRestore(
self,
data: "BackupDataInput",
appVersionCode: int,
newState: "BackupDataOutput",
):
"""执行恢复操作"""
pass
def toString(self) -> str:
return f"BackupAgent@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataInput
# ═══════════════════════════════════════════════════════════════
class BackupDataInput(Stub):
"""BackupDataInput — 备份数据输入流"""
def __init__(self, filepath: str = ""):
super().__init__()
self._filepath = filepath
self._data: Dict[str, Any] = {}
self._keys: List[str] = []
self._index = 0
if filepath and _os.path.exists(filepath):
with open(filepath, "r") as f:
self._data = json.load(f)
self._keys = list(self._data.keys())
def readNextHeader(self) -> bool:
"""读取下一个数据头"""
if self._index < len(self._keys):
return True
return False
def getKey(self) -> str:
"""获取当前键名"""
if self._index < len(self._keys):
return self._keys[self._index]
return ""
def getDataSize(self) -> int:
"""获取当前数据大小"""
if self._index < len(self._keys):
val = self._data[self._keys[self._index]]
return len(str(val))
return 0
def readEntityData(self, data: bytearray, offset: int, size: int) -> int:
"""读取实体数据"""
if self._index < len(self._keys):
val = str(self._data[self._keys[self._index]])
raw = val.encode("utf-8")
copy_len = min(size, len(raw) - offset)
for i in range(copy_len):
data[offset + i] = raw[i]
self._index += 1
return copy_len
return -1
def skipEntityData(self):
"""跳过当前实体数据"""
self._index += 1
def toString(self) -> str:
return f"BackupDataInput(file={self._filepath})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataOutput
# ═══════════════════════════════════════════════════════════════
class BackupDataOutput(Stub):
"""BackupDataOutput — 备份数据输出流"""
def __init__(self, filepath: str = ""):
super().__init__()
self._filepath = filepath
self._data: Dict[str, str] = {}
def writeEntityHeader(self, key: str, dataSize: int):
"""写入实体数据头"""
pass
def writeEntityData(self, data: bytes, size: int):
"""写入实体数据"""
key = f"entity_{len(self._data)}"
self._data[key] = data[:size].decode("utf-8", errors="replace")
if self._filepath:
_os.makedirs(_os.path.dirname(self._filepath), exist_ok=True)
with open(self._filepath, "w") as f:
json.dump(self._data, f)
def toString(self) -> str:
return f"BackupDataOutput(file={self._filepath})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# FullBackupDataOutput
# ═══════════════════════════════════════════════════════════════
class FullBackupDataOutput(Stub):
"""FullBackupDataOutput — 完整备份数据输出"""
def __init__(self, quota: int = 0, transportFlags: int = 0):
super().__init__()
self._quota = quota
self._transportFlags = transportFlags
def getQuota(self) -> int:
return self._quota
def getTransportFlags(self) -> int:
return self._transportFlags
def toString(self) -> str:
return f"FullBackupDataOutput(quota={self._quota})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupHelper
# ═══════════════════════════════════════════════════════════════
class BackupHelper(Stub):
"""BackupHelper — 备份辅助接口"""
def __init__(self):
super().__init__()
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行备份"""
raise AndroidOnlyError("BackupHelper.performBackup() 仅在 Android 设备上可用")
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复实体"""
raise AndroidOnlyError("BackupHelper.restoreEntity() 仅在 Android 设备上可用")
def toString(self) -> str:
return f"BackupHelper@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# FileBackupHelper
# ═══════════════════════════════════════════════════════════════
class FileBackupHelper(BackupHelper):
"""FileBackupHelper — 文件备份辅助"""
def __init__(self, context: Any, files: List[str]):
super().__init__()
self._context = context
self._files = list(files)
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行文件备份"""
for fname in self._files:
if self._context:
filepath = _os.path.join(self._context.getFilesDir(), fname)
if _os.path.exists(filepath):
with open(filepath, "rb") as f:
content = f.read()
data.writeEntityHeader(fname, len(content))
data.writeEntityData(content, len(content))
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复文件实体"""
key = data.getKey()
if self._context and key in self._files:
filepath = _os.path.join(self._context.getFilesDir(), key)
size = data.getDataSize()
buf = bytearray(size)
data.readEntityData(buf, 0, size)
_os.makedirs(_os.path.dirname(filepath), exist_ok=True)
with open(filepath, "wb") as f:
f.write(buf)
def toString(self) -> str:
return f"FileBackupHelper(files={self._files})"
def equals(self, other) -> bool:
if not isinstance(other, FileBackupHelper):
return False
return self._files == other._files
def hashCode(self) -> int:
return hash(tuple(self._files)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# SharedPreferencesBackupHelper
# ═══════════════════════════════════════════════════════════════
class SharedPreferencesBackupHelper(BackupHelper):
"""SharedPreferencesBackupHelper — SharedPreferences 备份辅助"""
def __init__(self, context: Any, prefNames: List[str]):
super().__init__()
self._context = context
self._prefNames = list(prefNames)
def performBackup(
self,
oldState: "BackupDataInput",
data: "BackupDataOutput",
newState: "BackupDataOutput",
):
"""执行 SharedPreferences 备份"""
if not self._context:
return
prefs_dir = _os.path.join(_app_data_dir, "shared_prefs")
for name in self._prefNames:
filepath = _os.path.join(prefs_dir, f"{name}.xml")
if _os.path.exists(filepath):
with open(filepath, "rb") as f:
content = f.read()
data.writeEntityHeader(name, len(content))
data.writeEntityData(content, len(content))
def restoreEntity(self, data: "BackupDataInput", appVersionCode: int):
"""恢复 SharedPreferences"""
key = data.getKey()
if self._context and key in self._prefNames:
prefs_dir = _os.path.join(_app_data_dir, "shared_prefs")
filepath = _os.path.join(prefs_dir, f"{key}.xml")
size = data.getDataSize()
buf = bytearray(size)
data.readEntityData(buf, 0, size)
_os.makedirs(prefs_dir, exist_ok=True)
with open(filepath, "wb") as f:
f.write(buf)
def toString(self) -> str:
return f"SharedPreferencesBackupHelper(prefs={self._prefNames})"
def equals(self, other) -> bool:
if not isinstance(other, SharedPreferencesBackupHelper):
return False
return self._prefNames == other._prefNames
def hashCode(self) -> int:
return hash(tuple(self._prefNames)) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# RestoreObserver
# ═══════════════════════════════════════════════════════════════
class RestoreObserver(Stub):
"""RestoreObserver — 恢复过程观察者"""
def __init__(self):
super().__init__()
def onUpdate(self, nowBeingRestored: int, currentPackage: str):
"""恢复进度更新回调"""
pass
def onRestoreStarting(self, numPackages: int):
"""恢复开始回调"""
pass
def onRestoreFinished(self, error: int):
"""恢复完成回调"""
pass
def toString(self) -> str:
return f"RestoreObserver@{hex(id(self))}"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF
# ═══════════════════════════════════════════════════════════════
# BackupDataInputStream
# ═══════════════════════════════════════════════════════════════
class BackupDataInputStream(Stub):
"""BackupDataInputStream — 备份数据输入流包装器"""
def __init__(self, dataInput: "BackupDataInput" = None):
super().__init__()
self._input = dataInput
self._pos = 0
self._size = 0
def read(self) -> int:
"""读取一个字节"""
if self._input and self._pos < self._size:
self._pos += 1
return 0
return -1
def readFully(self, data: bytearray, offset: int, length: int):
"""读取完整数据块"""
if self._input:
self._input.readEntityData(data, offset, length)
def size(self) -> int:
return self._size
def toString(self) -> str:
return f"BackupDataInputStream(size={self._size})"
def equals(self, other) -> bool:
return self is other
def hashCode(self) -> int:
return id(self) & 0x7FFFFFFF