Coverage for kea/app_hm.py: 76%
96 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-22 16:05 +0800
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-22 16:05 +0800
1import logging
2import os
3import hashlib
4import subprocess, shlex
5import zipfile
6import json
7import shutil
9from typing import TYPE_CHECKING
10if TYPE_CHECKING:
11 from .start import Setting
13if __name__ != "__main__":
14 from .intent import Intent
16def run_cmd(cmd):
17 return subprocess.run(shlex.split(cmd), shell=True, check=True).stdout
19class AppHM(object):
20 """
21 this class describes an app
22 """
24 def __init__(self, app_path, output_dir=None, settings:"Setting"=None):
25 """
26 create an App instance
27 :param app_path: local file path of app
28 :return:
29 """
30 # assert app_path is not None
31 self.logger = logging.getLogger(self.__class__.__name__)
33 self.app_path = app_path
35 self.output_dir = output_dir
36 if output_dir is not None:
37 if not os.path.isdir(output_dir):
38 os.makedirs(output_dir)
40 self.settings = settings
42 if not self.settings.is_package:
43 self._hap_init()
44 else:
45 self._package_init(package_name=app_path)
47 def _package_init(self, package_name):
48 self.package_name = package_name
49 self.main_activity = self._dumpsys_package_info
51 @property
52 def _dumpsys_package_info(self):
53 from .adapter.hdc import HDC_EXEC
54 cmd = [HDC_EXEC, "-t", self.settings.device_serial, "shell", "bm", "dump", "-n", self.package_name]
55 r = subprocess.check_output(cmd, text=True)
56 package_info_str = r.split("\n", maxsplit=1)[-1]
57 import json
58 package_info = json.loads(package_info_str)
59 return package_info["hapModuleInfos"][0]["mainAbility"]
61 def __str__(self) -> str:
62 app_info = ", ".join([
63 f"bundleName:{self.package_name}",
64 f"total_abilities:{self.total_activities}",
65 f"main_ability:{self.main_activity}",
66 ])
67 return f"App({app_info})"
69 def _hap_init(self):
70 self.logger.info(f"Extracting info from {self.app_path}")
71 self.logger.info(f"Hapfile is {os.path.basename(self.app_path)}")
72 # make temp dir
73 temp_dir = os.path.join(os.path.dirname(self.app_path), "temp_hap")
74 # temp_dir = "/".join(self.app_path.split("/")[:-1]) + "/temp_hap"
75 if os.path.exists(temp_dir):
76 shutil.rmtree(temp_dir)
77 os.mkdir(temp_dir)
79 with zipfile.ZipFile(self.app_path, 'r') as zip_ref:
80 zip_ref.extractall(temp_dir)
82 with open(temp_dir + "/module.json") as f:
83 moudle_json = json.load(f)
85 with open(temp_dir + "/pack.info") as f:
86 pack_info = json.load(f)
88 self.load_hap_info(moudle_json, pack_info)
90 shutil.rmtree(temp_dir)
92 def load_hap_info(self, module_json, pack_info):
93 self.package_name = pack_info["summary"]["app"]["bundleName"]
94 self.api_version = pack_info["summary"]["modules"][0]["apiVersion"]["target"]
95 self.main_activity = pack_info["summary"]["modules"][0]["mainAbility"]
96 self.activities = pack_info["summary"]["modules"][0]["abilities"]
97 self.total_activities = len(self.activities)
98 # self.possible_broadcasts = self.get_possible_broadcasts()
99 self.hashes = self.get_hashes()
101 def get_package_name(self):
102 """
103 get package name of current app
104 :return:
105 """
106 return self.package_name
108 def get_main_activity(self):
109 """
110 get package name of current app
111 :return:
112 """
113 if self.main_activity is not None:
114 return self.main_activity
115 else:
116 self.logger.warning("Cannot get main activity from manifest.")
117 # return self.dumpsys_main_activity
119 def get_start_intent(self):
120 """
121 get an intent to start the app
122 :return: Intent
123 """
124 bundle_name = self.get_package_name()
125 main_ability = self.get_main_activity()
126 # hdc shell aa -b [bundleName] -a [Main ability]
127 return Intent(suffix="-b {} -a {}".format(bundle_name, main_ability), is_harmonyos=True)
129 # def get_start_with_profiling_intent(self, trace_file, sampling=None):
130 # """
131 # get an intent to start the app with profiling
132 # :return: Intent
133 # """
134 # package_name = self.get_package_name()
135 # if self.get_main_activity():
136 # package_name += "/%s" % self.get_main_activity()
137 # if sampling is not None:
138 # return Intent(prefix="start --start-profiler %s --sampling %d" % (trace_file, sampling), suffix=package_name)
139 # else:
140 # return Intent(prefix="start --start-profiler %s" % trace_file, suffix=package_name)
142 def get_stop_intent(self):
143 """
144 get an intent to stop the app
145 :return: Intent
146 """
147 bundle_name = self.get_package_name()
148 return Intent(prefix="force-stop", suffix=bundle_name, is_harmonyos=True)
150 # def get_possible_broadcasts(self):
151 # possible_broadcasts = set()
152 # for receiver in self.apk.get_receivers():
153 # intent_filters = self.apk.get_intent_filters('receiver', receiver)
154 # actions = intent_filters['action'] if 'action' in intent_filters else []
155 # categories = intent_filters['category'] if 'category' in intent_filters else []
156 # categories.append(None)
157 # for action in actions:
158 # for category in categories:
159 # intent = Intent(prefix='broadcast', action=action, category=category)
160 # possible_broadcasts.add(intent)
161 # return possible_broadcasts
163 def get_hashes(self, block_size=2 ** 8):
164 """
165 Calculate MD5,SHA-1, SHA-256
166 hashes of APK input file
167 @param block_size:
168 """
169 md5 = hashlib.md5()
170 sha1 = hashlib.sha1()
171 sha256 = hashlib.sha256()
172 f = open(self.app_path, 'rb')
173 while True:
174 data = f.read(block_size)
175 if not data:
176 break
177 md5.update(data)
178 sha1.update(data)
179 sha256.update(data)
180 return [md5.hexdigest(), sha1.hexdigest(), sha256.hexdigest()]
182if __name__ == "__main__":
183 import argparse
184 parser = argparse.ArgumentParser(description="agr parser for Extrating info from hap")
185 parser.add_argument("-a", "--app", type=str, help="the address to the .hap file", required=True)
186 args = parser.parse_args()
187 app = AppHM(args.app)
188 # * uncomment the below block to save the ablity info to file
189 # with open(app.package_name+".info.json", "w") as fp:
190 # import json
191 # json.dump(app.activities, fp, indent=4, sort_keys=True)
192 print(app)