114 lines
3.8 KiB
Python
114 lines
3.8 KiB
Python
"""
|
||
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保持为空
|
||
|