Files
Android-pyApi/android_pyapi/android/app_usage.py
T
shuiyu1123 506f9ac725 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 行代码
2026-06-24 12:00:28 +00:00

527 lines
19 KiB
Python

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