""" 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