- 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关键字冲突
721 lines
19 KiB
Python
721 lines
19 KiB
Python
"""
|
|
Android dalvik.system APIs implemented in Python.
|
|
dalvik.system package - DexClassLoader, PathClassLoader, DexFile, VMRuntime, VMDebug, etc.
|
|
Desktop stub implementation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .._core import Stub, AndroidOnlyError
|
|
import sys as _sys
|
|
import os as _os
|
|
from typing import List, Optional
|
|
|
|
__all__ = [
|
|
"DexClassLoader", "PathClassLoader", "BaseDexClassLoader",
|
|
"DexFile", "InMemoryDexClassLoader",
|
|
"VMRuntime", "VMDebug",
|
|
"CloseGuard", "PotentialDeadlockError",
|
|
"BlockGuard",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# BaseDexClassLoader
|
|
# ============================================================
|
|
|
|
class BaseDexClassLoader(Stub):
|
|
"""
|
|
dalvik.system.BaseDexClassLoader
|
|
A class loader that loads classes from DEX files.
|
|
"""
|
|
|
|
def __init__(self, dex_path: str = "", optimized_dir: str = None,
|
|
library_search_path: str = None, parent: Any = None):
|
|
super().__init__()
|
|
self._dex_path = dex_path
|
|
self._optimized_dir = optimized_dir
|
|
self._library_search_path = library_search_path
|
|
self._parent = parent or type.__subclasses__
|
|
self._loaded_classes = set()
|
|
self._assertion_status = {}
|
|
self._default_assertion_status = False
|
|
|
|
def loadClass(self, name: str, resolve: bool = True) -> Optional[type]:
|
|
"""Loads a class by name."""
|
|
# Try to find the class in the Python path
|
|
parts = name.split(".")
|
|
module_name = ".".join(parts[:-1])
|
|
class_name = parts[-1]
|
|
try:
|
|
if module_name:
|
|
module = __import__(module_name, fromlist=[class_name])
|
|
cls = getattr(module, class_name, None)
|
|
else:
|
|
cls = globals().get(class_name) or __builtins__.get(class_name)
|
|
if cls:
|
|
self._loaded_classes.add(name)
|
|
return cls
|
|
return None
|
|
except (ImportError, AttributeError):
|
|
return None
|
|
|
|
def findClass(self, name: str) -> Optional[type]:
|
|
"""Finds a class by name."""
|
|
return self.loadClass(name)
|
|
|
|
def findResource(self, name: str) -> Optional[str]:
|
|
"""Finds a resource by name."""
|
|
path = _os.path.join(self._dex_path or "", name)
|
|
if _os.path.exists(path):
|
|
return path
|
|
return None
|
|
|
|
def findResources(self, name: str) -> List[str]:
|
|
"""Finds all resources by name."""
|
|
result = []
|
|
r = self.findResource(name)
|
|
if r:
|
|
result.append(r)
|
|
return result
|
|
|
|
def getParent(self):
|
|
return self._parent
|
|
|
|
def getPackage(self, name: str):
|
|
"""Gets a package by name."""
|
|
return None
|
|
|
|
def setClassAssertionStatus(self, class_name: str, enabled: bool):
|
|
self._assertion_status[class_name] = enabled
|
|
|
|
def setDefaultAssertionStatus(self, enabled: bool):
|
|
self._default_assertion_status = enabled
|
|
|
|
def setPackageAssertionStatus(self, package_name: str, enabled: bool):
|
|
pass
|
|
|
|
def clearAssertionStatus(self):
|
|
self._assertion_status = {}
|
|
self._default_assertion_status = False
|
|
|
|
def toString(self) -> str:
|
|
return f"BaseDexClassLoader@{hex(id(self))}[dex={self._dex_path}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# DexClassLoader
|
|
# ============================================================
|
|
|
|
class DexClassLoader(BaseDexClassLoader):
|
|
"""
|
|
dalvik.system.DexClassLoader
|
|
A class loader that loads classes from .jar and .apk files.
|
|
"""
|
|
|
|
def __init__(self, dex_path: str = "", optimized_dir: str = None,
|
|
library_search_path: str = None, parent: Any = None):
|
|
super().__init__(dex_path, optimized_dir, library_search_path, parent)
|
|
|
|
def loadClass(self, name: str, resolve: bool = True) -> Optional[type]:
|
|
return super().loadClass(name, resolve)
|
|
|
|
def findClass(self, name: str) -> Optional[type]:
|
|
return super().findClass(name)
|
|
|
|
def getParent(self):
|
|
return super().getParent()
|
|
|
|
def getResourceAsStream(self, name: str):
|
|
"""Returns an input stream for reading a resource."""
|
|
path = self.findResource(name)
|
|
if path:
|
|
return open(path, 'rb')
|
|
return None
|
|
|
|
def getResources(self, name: str):
|
|
"""Returns an enumeration of URLs for a resource."""
|
|
return iter(self.findResources(name))
|
|
|
|
def toString(self) -> str:
|
|
return f"DexClassLoader@{hex(id(self))}[dex={self._dex_path}]"
|
|
|
|
|
|
# ============================================================
|
|
# PathClassLoader
|
|
# ============================================================
|
|
|
|
class PathClassLoader(BaseDexClassLoader):
|
|
"""
|
|
dalvik.system.PathClassLoader
|
|
A class loader for loading classes from the file system.
|
|
"""
|
|
|
|
def __init__(self, dex_path: str = "", parent: Any = None,
|
|
library_search_path: str = None):
|
|
super().__init__(dex_path, None, library_search_path, parent)
|
|
|
|
def loadClass(self, name: str, resolve: bool = True) -> Optional[type]:
|
|
return super().loadClass(name, resolve)
|
|
|
|
def findClass(self, name: str) -> Optional[type]:
|
|
return super().findClass(name)
|
|
|
|
def getParent(self):
|
|
return super().getParent()
|
|
|
|
def toString(self) -> str:
|
|
return f"PathClassLoader@{hex(id(self))}[dex={self._dex_path}]"
|
|
|
|
|
|
# ============================================================
|
|
# InMemoryDexClassLoader
|
|
# ============================================================
|
|
|
|
class InMemoryDexClassLoader(BaseDexClassLoader):
|
|
"""
|
|
dalvik.system.InMemoryDexClassLoader
|
|
A class loader that loads classes from in-memory DEX buffers.
|
|
"""
|
|
|
|
def __init__(self, dex_buffers: List, parent: Any = None):
|
|
super().__init__("", None, None, parent)
|
|
self._dex_buffers = dex_buffers
|
|
|
|
def loadClass(self, name: str, resolve: bool = True) -> Optional[type]:
|
|
return super().loadClass(name, resolve)
|
|
|
|
def findClass(self, name: str) -> Optional[type]:
|
|
return super().findClass(name)
|
|
|
|
def toString(self) -> str:
|
|
return f"InMemoryDexClassLoader@{hex(id(self))}[buffers={len(self._dex_buffers)}]"
|
|
|
|
|
|
# ============================================================
|
|
# DexFile
|
|
# ============================================================
|
|
|
|
class DexFile(Stub):
|
|
"""
|
|
dalvik.system.DexFile
|
|
Represents a single DEX file.
|
|
"""
|
|
|
|
def __init__(self, file_name: str = None):
|
|
super().__init__()
|
|
self._file_name = file_name
|
|
self._closed = False
|
|
|
|
@staticmethod
|
|
def loadDex(source_path: str, output_path: str, flags: int = 0) -> "DexFile":
|
|
return DexFile(source_path)
|
|
|
|
def loadClass(self, name: str, loader: Any = None) -> Optional[type]:
|
|
"""Loads a class from this DEX file."""
|
|
parts = name.split(".")
|
|
try:
|
|
module = __import__(".".join(parts[:-1]), fromlist=[parts[-1]])
|
|
return getattr(module, parts[-1], None)
|
|
except (ImportError, AttributeError):
|
|
return None
|
|
|
|
def loadClassBinaryName(self, name: str, loader: Any = None) -> Optional[type]:
|
|
"""Loads a class using binary (internal) name format."""
|
|
java_name = name.replace("/", ".")
|
|
return self.loadClass(java_name, loader)
|
|
|
|
def getName(self) -> str:
|
|
return self._file_name or ""
|
|
|
|
def close(self):
|
|
self._closed = True
|
|
|
|
def entries(self):
|
|
"""Returns an enumeration of class names."""
|
|
return iter([])
|
|
|
|
def getEntryCount(self) -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def isDexOptNeeded(source_path: str) -> bool:
|
|
return False
|
|
|
|
def toString(self) -> str:
|
|
return f"DexFile@{hex(id(self))}[file={self._file_name}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# VMRuntime
|
|
# ============================================================
|
|
|
|
class VMRuntime(Stub):
|
|
"""
|
|
dalvik.system.VMRuntime
|
|
Provides access to Dalvik/ART VM runtime features.
|
|
"""
|
|
|
|
_runtime = None
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._target_sdk_version = 0
|
|
self._jit_compiler_filter = "speed"
|
|
self._large_heap_class = None
|
|
self._large_heap_size = 0
|
|
self._native_allocation_count = 0
|
|
self._disabled_compat_changes = []
|
|
|
|
@staticmethod
|
|
def getRuntime() -> "VMRuntime":
|
|
if VMRuntime._runtime is None:
|
|
VMRuntime._runtime = VMRuntime()
|
|
return VMRuntime._runtime
|
|
|
|
def is64Bit(self) -> bool:
|
|
import struct
|
|
return struct.calcsize("P") == 8
|
|
|
|
def setTargetSdkVersion(self, version: int):
|
|
self._target_sdk_version = version
|
|
|
|
def getTargetSdkVersion(self) -> int:
|
|
return self._target_sdk_version
|
|
|
|
def startJitCompilation(self):
|
|
pass
|
|
|
|
def endJitCompilation(self):
|
|
pass
|
|
|
|
def concurrentGC(self, enable: bool):
|
|
pass
|
|
|
|
def enableGcLogging(self, enable: bool):
|
|
pass
|
|
|
|
def vmInstructionSet(self) -> str:
|
|
import platform
|
|
machine = platform.machine()
|
|
if machine in ("x86_64", "amd64"):
|
|
return "x86_64"
|
|
elif machine in ("i386", "i686"):
|
|
return "x86"
|
|
elif machine in ("aarch64", "arm64"):
|
|
return "arm64"
|
|
elif machine.startswith("arm"):
|
|
return "arm"
|
|
return machine
|
|
|
|
def isVm64(self) -> bool:
|
|
return self.is64Bit()
|
|
|
|
def getExprOpts(self) -> str:
|
|
return ""
|
|
|
|
def setExprOpts(self, opts: str):
|
|
pass
|
|
|
|
def registerAppInfo(self, app_name: str, app_data_dir: str):
|
|
pass
|
|
|
|
def getDetachThreadTimeout(self) -> int:
|
|
return 0
|
|
|
|
def setDetachThreadTimeout(self, timeout_ms: int):
|
|
pass
|
|
|
|
def setDisabledCompatChanges(self, changes: List[int]):
|
|
self._disabled_compat_changes = list(changes)
|
|
|
|
def isBootClassPathOnDisk(self) -> bool:
|
|
return True
|
|
|
|
def isJavaDebuggable(self) -> bool:
|
|
return False
|
|
|
|
def getJitCompilerFilter(self) -> str:
|
|
return self._jit_compiler_filter
|
|
|
|
def setJitCompilerFilter(self, filter_: str):
|
|
self._jit_compiler_filter = filter_
|
|
|
|
def getLargeHeapClass(self) -> str:
|
|
return self._large_heap_class
|
|
|
|
def setLargeHeapClass(self, class_name: str):
|
|
self._large_heap_class = class_name
|
|
|
|
def getLargeHeapSize(self) -> int:
|
|
return self._large_heap_size
|
|
|
|
def setLargeHeapSize(self, size: int):
|
|
self._large_heap_size = size
|
|
|
|
def registerNativeAllocation(self, bytes_: int):
|
|
self._native_allocation_count += bytes_
|
|
|
|
def registerNativeFree(self, bytes_: int):
|
|
self._native_allocation_count -= bytes_
|
|
|
|
def toString(self) -> str:
|
|
return f"VMRuntime@{hex(id(self))}[64bit={self.is64Bit()}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# VMDebug
|
|
# ============================================================
|
|
|
|
class VMDebug(Stub):
|
|
"""
|
|
dalvik.system.VMDebug
|
|
Provides debug features for the VM.
|
|
"""
|
|
TRACE_COUNT_ALLOCS: int = 1
|
|
TRACE_COUNT_ALLOCS_SYSTEM_ONLY: int = 2
|
|
TRACE_COUNT_ALLOCS_ALL: int = 3
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._alloc_count = 0
|
|
self._alloc_size = 0
|
|
self._freed_count = 0
|
|
self._freed_size = 0
|
|
|
|
@staticmethod
|
|
def startCountingAllocations():
|
|
pass
|
|
|
|
@staticmethod
|
|
def stopCountingAllocations():
|
|
pass
|
|
|
|
@staticmethod
|
|
def getGlobalAllocCount() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalAllocSize() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalFreedCount() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalFreedSize() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getLoadedClasses() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def printLoadedClasses(count: int):
|
|
pass
|
|
|
|
@staticmethod
|
|
def isDebuggerConnected() -> bool:
|
|
return False
|
|
|
|
@staticmethod
|
|
def isDebuggingEnabled() -> bool:
|
|
return False
|
|
|
|
@staticmethod
|
|
def resetAllocCount():
|
|
pass
|
|
|
|
@staticmethod
|
|
def getThreadAllocCount() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getThreadAllocSize() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def threadCpuTimeNanos() -> int:
|
|
import time
|
|
return int(time.time() * 1e9)
|
|
|
|
@staticmethod
|
|
def startEmulatorTracing():
|
|
pass
|
|
|
|
@staticmethod
|
|
def stopEmulatorTracing():
|
|
pass
|
|
|
|
@staticmethod
|
|
def startMethodTracing(filename: str = None, buffer_size: int = 0, flags: int = 0, sampling_interval: int = 0):
|
|
pass
|
|
|
|
@staticmethod
|
|
def stopMethodTracing():
|
|
pass
|
|
|
|
@staticmethod
|
|
def getMethodTracingMode() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getRuntimeStat(stat_name: str) -> str:
|
|
return ""
|
|
|
|
@staticmethod
|
|
def getRuntimeStatValue(stat_name: str, value: int) -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def resolveClassLinkageError(cls: type):
|
|
pass
|
|
|
|
@staticmethod
|
|
def crash():
|
|
raise RuntimeError("VMDebug.crash() called")
|
|
|
|
@staticmethod
|
|
def dumpReferenceTables():
|
|
pass
|
|
|
|
@staticmethod
|
|
def setAllocTrackerStackDepth(depth: int):
|
|
pass
|
|
|
|
@staticmethod
|
|
def attachAgent(agent: str, options: str = None, class_loader: Any = None):
|
|
pass
|
|
|
|
@staticmethod
|
|
def startAllocCounting():
|
|
pass
|
|
|
|
@staticmethod
|
|
def stopAllocCounting():
|
|
pass
|
|
|
|
@staticmethod
|
|
def getGlobalExternalAllocCount() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalExternalAllocSize() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalExternalFreedCount() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def getGlobalExternalFreedSize() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def setAllocationLimit(limit: int):
|
|
pass
|
|
|
|
@staticmethod
|
|
def setGlobalCpuLimit(limit: int):
|
|
pass
|
|
|
|
@staticmethod
|
|
def setThreadCpuLimit(limit: int):
|
|
pass
|
|
|
|
@staticmethod
|
|
def setMethodTracingPriority(priority: int):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"VMDebug@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# CloseGuard
|
|
# ============================================================
|
|
|
|
class CloseGuard(Stub):
|
|
"""
|
|
dalvik.system.CloseGuard
|
|
Reports when resources are not closed properly.
|
|
"""
|
|
REPORTER = None
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._open = False
|
|
self._reported = False
|
|
|
|
@staticmethod
|
|
def get() -> "CloseGuard":
|
|
return CloseGuard()
|
|
|
|
def open(self, closer: str):
|
|
"""Marks this guard as open."""
|
|
self._open = True
|
|
|
|
def close(self):
|
|
"""Marks this guard as closed."""
|
|
self._open = False
|
|
|
|
def warnIfOpen(self):
|
|
"""Warns if the guard is still open."""
|
|
if self._open and not self._reported:
|
|
self._reported = True
|
|
|
|
@staticmethod
|
|
def setEnabled(enabled: bool):
|
|
pass
|
|
|
|
@staticmethod
|
|
def getReporter():
|
|
return CloseGuard.REPORTER
|
|
|
|
@staticmethod
|
|
def setReporter(reporter):
|
|
CloseGuard.REPORTER = reporter
|
|
|
|
def toString(self) -> str:
|
|
return f"CloseGuard@{hex(id(self))}[open={self._open}]"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# PotentialDeadlockError
|
|
# ============================================================
|
|
|
|
class PotentialDeadlockError(Exception, Stub):
|
|
"""
|
|
dalvik.system.PotentialDeadlockError
|
|
Thrown when a potential deadlock is detected.
|
|
"""
|
|
def __init__(self, message: str = None):
|
|
super().__init__(message or "Potential deadlock detected")
|
|
self._message = message
|
|
|
|
def toString(self) -> str:
|
|
return f"PotentialDeadlockError: {self._message}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return isinstance(other, PotentialDeadlockError) and self._message == other._message
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self._message) & 0x7FFFFFFF
|
|
|
|
|
|
# ============================================================
|
|
# BlockGuard
|
|
# ============================================================
|
|
|
|
class BlockGuard(Stub):
|
|
"""
|
|
dalvik.system.BlockGuard
|
|
Mechanism to detect and prevent blocking operations on the main thread.
|
|
"""
|
|
LAX_POLICY = None
|
|
|
|
class Policy(Stub):
|
|
"""
|
|
dalvik.system.BlockGuard.Policy
|
|
Interface for block guard policies.
|
|
"""
|
|
def onReadFromDisk(self): pass
|
|
def onWriteToDisk(self): pass
|
|
def onNetwork(self): pass
|
|
def onExplicitSuspend(self): pass
|
|
def getPolicyMask(self) -> int: return 0
|
|
def onFileSystemAccess(self): pass
|
|
def onUnbufferedIO(self): pass
|
|
|
|
def toString(self) -> str:
|
|
return "BlockGuard.Policy"
|
|
|
|
def equals(self, other) -> bool:
|
|
return isinstance(other, BlockGuard.Policy)
|
|
|
|
def hashCode(self) -> int:
|
|
return hash("BlockGuard.Policy") & 0x7FFFFFFF
|
|
|
|
class Operational(Policy):
|
|
"""
|
|
dalvik.system.BlockGuard.Operational
|
|
Operational policy for block guard.
|
|
"""
|
|
def toString(self) -> str:
|
|
return "BlockGuard.Operational"
|
|
|
|
def equals(self, other) -> bool:
|
|
return isinstance(other, BlockGuard.Operational)
|
|
|
|
def hashCode(self) -> int:
|
|
return hash("BlockGuard.Operational") & 0x7FFFFFFF
|
|
|
|
_delegate = None
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
@staticmethod
|
|
def setDelegate(delegate):
|
|
BlockGuard._delegate = delegate
|
|
|
|
@staticmethod
|
|
def isOperational() -> bool:
|
|
return BlockGuard._delegate is not None
|
|
|
|
@staticmethod
|
|
def initDelegate():
|
|
"""Initializes the delegate as a BlockGuard.Operational."""
|
|
BlockGuard._delegate = BlockGuard.Operational()
|
|
|
|
@staticmethod
|
|
def getThreadPolicy():
|
|
"""Returns the policy for the current thread."""
|
|
return BlockGuard.LAX_POLICY or BlockGuard.Policy()
|
|
|
|
@staticmethod
|
|
def setThreadPolicy(policy: Policy):
|
|
pass
|
|
|
|
def toString(self) -> str:
|
|
return f"BlockGuard@{hex(id(self))}"
|
|
|
|
def equals(self, other) -> bool:
|
|
return self is other
|
|
|
|
def hashCode(self) -> int:
|
|
return hash(self) & 0x7FFFFFFF
|
|
|
|
|
|
BlockGuard.LAX_POLICY = BlockGuard.Policy() |