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:
1 parent
8ad62be068
commit
506f9ac725
43 files changed
+16704
-23
No files matched your search
@@ -0,0 +1,527 @@
|
||||
"""
|
||||
Android Usage Stats API — UsageStatsManager, UsageStats, UsageEvents, ConfigurationStats, StorageStatsManager
|
||||
方法签名完全对齐 Android SDK
|
||||
"""
|
||||
|
||||
import json
|
||||
import os as _os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Callable
|
||||
from collections import defaultdict
|
||||
|
||||
from .._core import Stub, AndroidOnlyError, resolve_android_path, _app_data_dir, _sdcard_root
|
||||
from ..java.lang import RuntimeException
|
||||
|
||||
|
||||
IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {})
|
||||
|
||||
__all__ = [
|
||||
"UsageStatsManager",
|
||||
"UsageStats",
|
||||
"UsageEvents",
|
||||
"ConfigurationStats",
|
||||
"CacheUsageStats",
|
||||
"NetworkUsageStats",
|
||||
"UsageStatsManagerInternal",
|
||||
"StorageStatsManager",
|
||||
"StorageStats",
|
||||
]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# UsageStatsManager
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class UsageStatsManager(Stub):
|
||||
"""UsageStatsManager — 使用统计管理器 (桌面模拟)"""
|
||||
|
||||
INTERVAL_DAILY = 0
|
||||
INTERVAL_WEEKLY = 1
|
||||
INTERVAL_MONTHLY = 2
|
||||
INTERVAL_YEARLY = 3
|
||||
INTERVAL_BEST = 4
|
||||
INTERVAL_COUNT = 5
|
||||
|
||||
USAGE_SOURCE_UNSPECIFIED = 0
|
||||
USAGE_SOURCE_CURRENT_ACTIVITY = 1
|
||||
USAGE_SOURCE_TASK_ROOT_ACTIVITY = 2
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._app_inactive: Dict[str, bool] = {}
|
||||
|
||||
def queryUsageStats(
|
||||
self, intervalType: int, beginTime: int, endTime: int
|
||||
) -> List["UsageStats"]:
|
||||
"""查询使用统计信息"""
|
||||
result: List[UsageStats] = []
|
||||
stats_dir = _os.path.join(_app_data_dir, "usagestats")
|
||||
if _os.path.exists(stats_dir):
|
||||
for fname in _os.listdir(stats_dir):
|
||||
if fname.endswith(".json"):
|
||||
filepath = _os.path.join(stats_dir, fname)
|
||||
try:
|
||||
with open(filepath, "r") as f:
|
||||
data = json.load(f)
|
||||
stats = UsageStats()
|
||||
stats._package_name = data.get("package", fname[:-5])
|
||||
stats._first_time = data.get("first_time", beginTime)
|
||||
stats._last_time = data.get("last_time", endTime)
|
||||
stats._last_used = data.get("last_used", beginTime)
|
||||
stats._total_foreground = data.get("total_foreground", 0)
|
||||
stats._launch_count = data.get("launch_count", 0)
|
||||
if stats._last_time >= beginTime and stats._last_time <= endTime:
|
||||
result.append(stats)
|
||||
except (json.JSONDecodeError, IOError):
|
||||
pass
|
||||
return result
|
||||
|
||||
def queryConfigurations(
|
||||
self, intervalType: int, beginTime: int, endTime: int
|
||||
) -> List["ConfigurationStats"]:
|
||||
"""查询配置统计"""
|
||||
return []
|
||||
|
||||
def queryEvents(
|
||||
self, beginTime: int, endTime: int
|
||||
) -> "UsageEvents":
|
||||
"""查询使用事件"""
|
||||
return UsageEvents()
|
||||
|
||||
def isAppInactive(self, packageName: str) -> bool:
|
||||
"""检查应用是否处于非活跃状态"""
|
||||
return self._app_inactive.get(packageName, False)
|
||||
|
||||
def setAppInactive(self, packageName: str, inactive: bool):
|
||||
"""设置应用非活跃状态"""
|
||||
self._app_inactive[packageName] = inactive
|
||||
|
||||
def onCarrierPrivilegedAppsChanged(self):
|
||||
"""运营商特权应用变更 (桌面存根)"""
|
||||
pass
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"UsageStatsManager@{hex(id(self))}"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
return self is other
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return id(self) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# UsageStats
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class UsageStats(Stub):
|
||||
"""UsageStats — 应用使用统计"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._package_name = ""
|
||||
self._first_time = 0
|
||||
self._last_time = 0
|
||||
self._last_used = 0
|
||||
self._total_foreground = 0
|
||||
self._launch_count = 0
|
||||
|
||||
def getPackageName(self) -> str:
|
||||
return self._package_name
|
||||
|
||||
def getFirstTimeStamp(self) -> int:
|
||||
return self._first_time
|
||||
|
||||
def getLastTimeStamp(self) -> int:
|
||||
return self._last_time
|
||||
|
||||
def getLastUsedTime(self) -> int:
|
||||
return self._last_used
|
||||
|
||||
def getTotalTimeInForeground(self) -> int:
|
||||
return self._total_foreground
|
||||
|
||||
def getAppLaunchCount(self) -> int:
|
||||
return self._launch_count
|
||||
|
||||
def toString(self) -> str:
|
||||
return (f"UsageStats(pkg={self._package_name}, "
|
||||
f"foreground={self._total_foreground}ms, "
|
||||
f"launches={self._launch_count})")
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
if not isinstance(other, UsageStats):
|
||||
return False
|
||||
return self._package_name == other._package_name
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return hash(self._package_name) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# UsageEvents
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class UsageEvents(Stub):
|
||||
"""UsageEvents — 使用事件列表"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._events: List["UsageEvents.Event"] = []
|
||||
self._index = 0
|
||||
|
||||
def getNextEvent(self, eventOut: "UsageEvents.Event") -> bool:
|
||||
"""获取下一个事件"""
|
||||
if self._index < len(self._events):
|
||||
ev = self._events[self._index]
|
||||
eventOut._package_name = ev._package_name
|
||||
eventOut._class_name = ev._class_name
|
||||
eventOut._action = ev._action
|
||||
eventOut._time_stamp = ev._time_stamp
|
||||
self._index += 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def hasNextEvent(self) -> bool:
|
||||
"""检查是否还有下一个事件"""
|
||||
return self._index < len(self._events)
|
||||
|
||||
def resetToStart(self):
|
||||
"""重置到起始位置"""
|
||||
self._index = 0
|
||||
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# Event — 内部事件类
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
class Event(Stub):
|
||||
"""UsageEvents.Event — 使用事件"""
|
||||
|
||||
# --- Event action constants ---
|
||||
MOVE_TO_FOREGROUND = 1
|
||||
MOVE_TO_BACKGROUND = 2
|
||||
CONFIGURATION_CHANGE = 3
|
||||
SHORTCUT_INVOCATION = 4
|
||||
STANDBY_BUDGET_CHANGED = 5
|
||||
USER_INTERACTION = 6
|
||||
NOTIFICATION_SEEN = 7
|
||||
NOTIFICATION_INTERRUPTION = 8
|
||||
CHOOSER_ACTION = 9
|
||||
APP_COMPONENT_USED = 10
|
||||
ACTIVITY_STOPPED = 11
|
||||
SYSTEM_INTERACTION = 12
|
||||
FLAG_IS_PACKAGE_INSTALL = 13
|
||||
FLAG_NO_INTERRUPTION = 14
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._package_name = ""
|
||||
self._class_name = ""
|
||||
self._action = 0
|
||||
self._time_stamp = 0
|
||||
|
||||
def getPackageName(self) -> str:
|
||||
return self._package_name
|
||||
|
||||
def getClassName(self) -> str:
|
||||
return self._class_name
|
||||
|
||||
def getAction(self) -> int:
|
||||
return self._action
|
||||
|
||||
def getTimeStamp(self) -> int:
|
||||
return self._time_stamp
|
||||
|
||||
def toString(self) -> str:
|
||||
return (f"Event(pkg={self._package_name}, "
|
||||
f"action={self._action}, time={self._time_stamp})")
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
if not isinstance(other, UsageEvents.Event):
|
||||
return False
|
||||
return (self._package_name == other._package_name
|
||||
and self._action == other._action
|
||||
and self._time_stamp == other._time_stamp)
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return hash((self._package_name, self._action, self._time_stamp)) & 0x7FFFFFFF
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"UsageEvents(count={len(self._events)})"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
if not isinstance(other, UsageEvents):
|
||||
return False
|
||||
return self._events == other._events
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return hash(len(self._events)) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# ConfigurationStats
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class ConfigurationStats(Stub):
|
||||
"""ConfigurationStats — 配置统计"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._first_time = 0
|
||||
self._last_time = 0
|
||||
self._last_active = 0
|
||||
self._total_active = 0
|
||||
self._activation_count = 0
|
||||
|
||||
def getFirstTimeStamp(self) -> int:
|
||||
return self._first_time
|
||||
|
||||
def getLastTimeStamp(self) -> int:
|
||||
return self._last_time
|
||||
|
||||
def getLastTimeActive(self) -> int:
|
||||
return self._last_active
|
||||
|
||||
def getTotalTimeActive(self) -> int:
|
||||
return self._total_active
|
||||
|
||||
def getActivationCount(self) -> int:
|
||||
return self._activation_count
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"ConfigurationStats(active={self._total_active}ms, count={self._activation_count})"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
if not isinstance(other, ConfigurationStats):
|
||||
return False
|
||||
return (self._first_time == other._first_time
|
||||
and self._last_time == other._last_time)
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return hash((self._first_time, self._last_time)) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# CacheUsageStats
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class CacheUsageStats(Stub):
|
||||
"""CacheUsageStats — 缓存使用统计"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._cache_size = 0
|
||||
self._total_size = 0
|
||||
|
||||
def getCacheSize(self) -> int:
|
||||
return self._cache_size
|
||||
|
||||
def getTotalSize(self) -> int:
|
||||
return self._total_size
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"CacheUsageStats(cache={self._cache_size}B)"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
return self is other
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return id(self) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# NetworkUsageStats
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class NetworkUsageStats(Stub):
|
||||
"""NetworkUsageStats — 网络使用统计"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._rx_bytes = 0
|
||||
self._tx_bytes = 0
|
||||
self._rx_packets = 0
|
||||
self._tx_packets = 0
|
||||
|
||||
def getRxBytes(self) -> int:
|
||||
return self._rx_bytes
|
||||
|
||||
def getTxBytes(self) -> int:
|
||||
return self._tx_bytes
|
||||
|
||||
def getRxPackets(self) -> int:
|
||||
return self._rx_packets
|
||||
|
||||
def getTxPackets(self) -> int:
|
||||
return self._tx_packets
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"NetworkUsageStats(rx={self._rx_bytes}B, tx={self._tx_bytes}B)"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
return self is other
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return id(self) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# UsageStatsManagerInternal
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class UsageStatsManagerInternal(Stub):
|
||||
"""UsageStatsManagerInternal — 内部使用统计管理器"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def reportEvent(self, event: Any):
|
||||
"""报告使用事件"""
|
||||
pass
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"UsageStatsManagerInternal@{hex(id(self))}"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
return self is other
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return id(self) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# StorageStatsManager
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class StorageStatsManager(Stub):
|
||||
"""StorageStatsManager — 存储统计管理器"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def queryStatsForPackage(
|
||||
self, storageUuid: Any, packageName: str, userHandle: Any
|
||||
) -> "StorageStats":
|
||||
"""查询指定应用存储统计"""
|
||||
return self._query_stats(storageUuid, packageName)
|
||||
|
||||
def queryStatsForUid(
|
||||
self, storageUuid: Any, uid: int
|
||||
) -> "StorageStats":
|
||||
"""查询指定 UID 存储统计"""
|
||||
return self._query_stats(storageUuid, f"uid_{uid}")
|
||||
|
||||
def queryStatsForUser(
|
||||
self, storageUuid: Any, userHandle: Any
|
||||
) -> "StorageStats":
|
||||
"""查询指定用户存储统计"""
|
||||
return self._query_stats(storageUuid, f"user_{userHandle}")
|
||||
|
||||
def queryExternalStatsForUser(
|
||||
self, storageUuid: Any, userHandle: Any
|
||||
) -> "StorageStats":
|
||||
"""查询外部用户存储统计"""
|
||||
return self._query_stats(storageUuid, f"external_user_{userHandle}")
|
||||
|
||||
def getFreeBytes(self, storageUuid: Any) -> int:
|
||||
"""获取空闲字节数"""
|
||||
try:
|
||||
st = _os.statvfs(_sdcard_root)
|
||||
return st.f_bavail * st.f_frsize
|
||||
except (OSError, AttributeError):
|
||||
return 1024 * 1024 * 1024 # 默认 1GB
|
||||
|
||||
def getTotalBytes(self, storageUuid: Any) -> int:
|
||||
"""获取总字节数"""
|
||||
try:
|
||||
st = _os.statvfs(_sdcard_root)
|
||||
return st.f_blocks * st.f_frsize
|
||||
except (OSError, AttributeError):
|
||||
return 64 * 1024 * 1024 * 1024 # 默认 64GB
|
||||
|
||||
def _query_stats(self, storage_uuid: Any, tag: str) -> "StorageStats":
|
||||
"""内部查询存储统计"""
|
||||
stats = StorageStats()
|
||||
pkg_dir = _os.path.join(_app_data_dir, "files")
|
||||
if _os.path.exists(pkg_dir):
|
||||
for dirpath, dirnames, filenames in _os.walk(pkg_dir):
|
||||
for fname in filenames:
|
||||
fpath = _os.path.join(dirpath, fname)
|
||||
try:
|
||||
fsize = _os.path.getsize(fpath)
|
||||
ext = _os.path.splitext(fname)[1].lower()
|
||||
if ext in (".jpg", ".png", ".gif", ".webp", ".bmp"):
|
||||
stats._image_bytes += fsize
|
||||
elif ext in (".mp3", ".wav", ".flac", ".ogg", ".aac"):
|
||||
stats._music_bytes += fsize
|
||||
elif ext in (".mp4", ".avi", ".mkv", ".mov", ".webm"):
|
||||
stats._video_bytes += fsize
|
||||
else:
|
||||
stats._data_bytes += fsize
|
||||
except OSError:
|
||||
pass
|
||||
return stats
|
||||
|
||||
def toString(self) -> str:
|
||||
return f"StorageStatsManager@{hex(id(self))}"
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
return self is other
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return id(self) & 0x7FFFFFFF
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# StorageStats
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
class StorageStats(Stub):
|
||||
"""StorageStats — 存储统计"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._app_bytes = 0
|
||||
self._data_bytes = 0
|
||||
self._cache_bytes = 0
|
||||
self._music_bytes = 0
|
||||
self._video_bytes = 0
|
||||
self._image_bytes = 0
|
||||
self._game_bytes = 0
|
||||
|
||||
def getAppBytes(self) -> int:
|
||||
return self._app_bytes
|
||||
|
||||
def getDataBytes(self) -> int:
|
||||
return self._data_bytes
|
||||
|
||||
def getCacheBytes(self) -> int:
|
||||
return self._cache_bytes
|
||||
|
||||
def getMusicBytes(self) -> int:
|
||||
return self._music_bytes
|
||||
|
||||
def getVideoBytes(self) -> int:
|
||||
return self._video_bytes
|
||||
|
||||
def getImageBytes(self) -> int:
|
||||
return self._image_bytes
|
||||
|
||||
def getGameBytes(self) -> int:
|
||||
return self._game_bytes
|
||||
|
||||
def toString(self) -> str:
|
||||
return (f"StorageStats(app={self._app_bytes}B, data={self._data_bytes}B, "
|
||||
f"cache={self._cache_bytes}B, music={self._music_bytes}B, "
|
||||
f"video={self._video_bytes}B, image={self._image_bytes}B)")
|
||||
|
||||
def equals(self, other) -> bool:
|
||||
if not isinstance(other, StorageStats):
|
||||
return False
|
||||
return (self._app_bytes == other._app_bytes
|
||||
and self._data_bytes == other._data_bytes)
|
||||
|
||||
def hashCode(self) -> int:
|
||||
return hash((self._app_bytes, self._data_bytes)) & 0x7FFFFFFF
|
||||
Reference in new issue
Block a user