""" Android App Module Android-specific application model. """ import logging import os from .android_intent import AndroidIntent from ...core.abstract_app import AbstractApp from ...exceptions import FATAL_EXCEPTIONS class AndroidApp(AbstractApp): """ Android 应用类 - 用于解析和管理 APK 文件信息 继承自 AbstractApp,实现 Android 特定的应用管理功能。 """ def __init__(self, package_name, output_dir=None): """ 创建 AndroidApp 实例 :param package_name: 应用包名 :param output_dir: 输出目录路径 """ super().__init__(output_dir=output_dir) self.app_path = None self.output_dir = output_dir if output_dir is not None: if not os.path.isdir(output_dir): os.makedirs(output_dir) self.package_name = package_name self._main_activity = None self._activities = [] self.dumpsys_main_activity = None @property def identifier(self) -> str: """获取应用唯一标识符(package_name)""" return self.package_name @property def main_activity(self) -> str: """获取主 Activity 名称""" if self._main_activity is not None: return self._main_activity else: if self.dumpsys_main_activity: self.logger.warning("Cannot get main activity from manifest. Using dumpsys result instead.") return self.dumpsys_main_activity return None @main_activity.setter def main_activity(self, value): """设置主 Activity""" self._main_activity = value @property def activities(self) -> list: """获取入口点列表(activities)""" return self._activities @activities.setter def activities(self, value): """设置 Activity 列表""" self._activities = value def get_start_with_profiling_intent(self, trace_file, sampling=None) -> 'AndroidIntent': """获取带 profiling 的启动 Intent""" package_name = self.package_name if self.main_activity: package_name += "/%s" % self.main_activity if sampling is not None: return AndroidIntent(prefix="start --start-profiler %s --sampling %d" % (trace_file, sampling), suffix=package_name) else: return AndroidIntent(prefix="start --start-profiler %s" % trace_file, suffix=package_name) def populate_activities(self, device): """ 使用 adb dumpsys package 获取所有 Activity """ import re self.logger.info(f"Populating activities for {self.package_name} via dumpsys...") try: cmd = f"dumpsys package {self.package_name}" output = device.adb.shell(cmd) # 匹配形如: [hash] package/activity pattern = re.compile(r'([0-9a-f]+)\s+' + re.escape(self.package_name) + r'/([^ \s]+)') matches = pattern.findall(output) new_activities = [] for m in matches: activity_full_name = f"{self.package_name}/{m[1]}" if activity_full_name not in new_activities: new_activities.append(activity_full_name) if new_activities: self.activities = sorted(new_activities) self.logger.info(f"Found {len(self.activities)} activities via dumpsys.") else: self.logger.warning("No activities found via dumpsys.") except FATAL_EXCEPTIONS: raise except Exception as e: self.logger.error(f"Failed to populate activities via dumpsys: {e}") # 非致命异常,activities保持为空