ld_operate.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import json
  2. import os
  3. from typing import TextIO
  4. from model.custom_struct import EmulatorInfo
  5. from tools.emulator.emulator import Emulator
  6. from tools.utils import Utils
  7. class LD(Emulator):
  8. def __init__(self, system_type: int):
  9. self.install_path = self.get_ld_install_dir(system_type)
  10. super().__init__(self.install_path)
  11. self.emulator_type = 'ld'
  12. self.system_type = system_type
  13. if not os.path.exists(self.install_path + r'\dnconsole.exe'):
  14. raise ValueError(f"{self.emulator_type}-{system_type},安装路径错误")
  15. def set_install_path(self, path: str):
  16. self.install_path = path
  17. def get_install_path(self) -> str:
  18. return self.install_path
  19. def dnc(self, cmd: str) -> str:
  20. return self._execute_cmd('dnconsole.exe" ' + cmd)
  21. def shell(self, index: int, cmd: str) -> str:
  22. return self._execute_cmd(f'ld.exe" -s {index} "{cmd}"').rstrip('\n')
  23. def start(self, index: int):
  24. self.dnc(f'launch --index {index}')
  25. def is_started(self, index) -> tuple[bool, EmulatorInfo | None]:
  26. emulator_info = self.get_info(index)
  27. if emulator_info :
  28. return emulator_info.is_enter_android == 1 , emulator_info
  29. return False, None
  30. def close(self, index: int):
  31. self.dnc(f'quit --index {index}')
  32. def close_all(self):
  33. self.dnc(f'quitall')
  34. def add(self, name: str):
  35. self.dnc(f'add --name {name}')
  36. def remove(self, index: int):
  37. self.dnc(f'remove --index {index}')
  38. def rename(self, index: int, name: str):
  39. self.dnc(f'rename --index {index} --title {name}')
  40. def restore(self, index: int, backup_file: str):
  41. self.dnc(f'restore --index {index} --file {backup_file}')
  42. def is_exists(self, index: int) -> bool:
  43. return os.path.exists(os.path.join(self.get_install_path(),'vms',f'leidian{index}'))
  44. def is_running(self, index: int) -> bool:
  45. return self.dnc(f'isrunning --index {index}') == 'running'
  46. def modify(self,index,cpu=None, memory=None, resolution=None, manufacturer=None, model=None, pnumber=None,
  47. imei=None, imsi=None, simserial=None, androidid=None, mac=None, autorotate=None, lockwindow=None, root=None):
  48. """
  49. 修改模拟器机型
  50. :param index: 模拟器索引
  51. :param args:
  52. [--resolution <w,h,dpi>]
  53. [--cpu <1 | 2 | 3 | 4>]
  54. [--memory <256 | 512 | 768 | 1024 | 1536 | 2048 | 4096 | 8192>]
  55. [--manufacturer asus]
  56. [--model ASUS_Z00DUO]
  57. [--pnumber 13800000000]
  58. [--imei <auto | 865166023949731>]
  59. [--imsi <auto | 460000000000000>]
  60. [--simserial <auto | 89860000000000000000>]
  61. [--androidid <auto | 0123456789abcdef>]
  62. [--mac <auto | 000000000000>]
  63. [--autorotate <1 | 0> 自动旋转
  64. [--lockwindow <1 | 0> 锁定窗口大小
  65. [--root <1 | 0>
  66. """
  67. formatted_args = " ".join([f"--{key} {value}" for key, value in locals().items() if value and key not in ['index', 'self']])
  68. self.dnc(f'modify --index {index} {formatted_args}')
  69. def get_manufacturer(self, index:int):
  70. return self.shell(index, 'getprop ro.product.manufacturer')
  71. def get_model(self,index:int):
  72. return self.shell(index, 'getprop ro.product.model')
  73. def get_android_version(self,index:int):
  74. return self.shell(index, 'getprop ro.build.version.release')
  75. def get_android_id(self, index:int):
  76. return self.shell(index, 'getprop phone.androidid')
  77. def get_IMEI(self, index:int):
  78. return self.shell(index, 'getprop phone.imei')
  79. def get_IMSI(self, index:int):
  80. return self.shell(index, 'getprop phone.imsi')
  81. def get_mac(self, index:int):
  82. if self.system_type == 9:
  83. return self.shell(index, 'cat /sys/class/net/wlan0/address')
  84. else:
  85. return self.shell(index, 'cat /sys/class/net/eth0/address')
  86. def get_resolution(self, index:int):
  87. return self.shell(index, 'wm size')
  88. def get_sim_serial(self, index:int):
  89. return self.shell(index, 'getprop phone.simserial')
  90. def get_phone_number(self, index:int):
  91. return self.shell(index, 'getprop phone.number')
  92. def get_ip(self, index:int):
  93. tmp_str = self.shell(index, 'ifconfig eth0')
  94. return Utils.extract_between_strings(tmp_str, 'ip ', ' ')
  95. def read_emulator_file(self, index: int, file_path: str) -> str:
  96. """
  97. 读取模拟器文本文件
  98. :param index: 模拟器索引
  99. :param file_path:模拟器文件路径
  100. :return:
  101. """
  102. return super().read_emulator_file(index, file_path)
  103. def write_emulator_file(self, index: int, file_path: str, content: str) -> bool:
  104. """
  105. 写入模拟器文本文件
  106. :param index: 模拟器索引
  107. :param file_path:模拟器文件路径
  108. :param content: 写入内容
  109. :return:
  110. """
  111. return super().write_emulator_file(index, file_path, content)
  112. def get_list(self) -> list[EmulatorInfo]:
  113. emu_list = []
  114. info_str = self.dnc('list2').strip()
  115. for line in info_str.split("\n"):
  116. tmp_list = line.split(',')
  117. if len(tmp_list) == 7:
  118. info = EmulatorInfo(
  119. index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]),
  120. bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]),
  121. pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=None,
  122. height=None, dpi=None
  123. )
  124. emu_list.append(info)
  125. elif len(tmp_list) == 10:
  126. info = EmulatorInfo(
  127. index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]),
  128. bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]),
  129. pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=int(tmp_list[7]),
  130. height=int(tmp_list[8]), dpi=int(tmp_list[9])
  131. )
  132. emu_list.append(info)
  133. return emu_list
  134. def get_info(self, index: int) -> EmulatorInfo | None:
  135. info_str = self.dnc('list2').strip()
  136. for line in info_str.split("\n"):
  137. if line:
  138. tmp_list = line.split(',')
  139. if int(tmp_list[0]) == index:
  140. if len(tmp_list) == 7:
  141. return EmulatorInfo(
  142. index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]),
  143. bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]),
  144. pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=None,
  145. height=None, dpi=None
  146. )
  147. elif len(tmp_list) == 10:
  148. return EmulatorInfo(
  149. index=int(tmp_list[0]), name=tmp_list[1], top_handle=int(tmp_list[2]),
  150. bind_handle=int(tmp_list[3]), is_enter_android=int(tmp_list[4]),
  151. pid=int(tmp_list[5]), vbox_pid=int(tmp_list[6]), width=int(tmp_list[7]),
  152. height=int(tmp_list[8]), dpi=int(tmp_list[9])
  153. )
  154. raise f"Emulator {index} not found"
  155. return None
  156. def set_share_dir(self, index: int, path: str, dir_type: int = 1) -> bool:
  157. """
  158. 设置模拟器的共享目录。
  159. :param index: 模拟器索引
  160. :param path: 共享目录路径
  161. :param dir_type: 共享目录类型(1: 图片, 2: 应用, 3: 杂项)
  162. :return: 成功返回 True,失败返回 False
  163. """
  164. # 共享目录类型映射
  165. key_map = {
  166. 1: 'statusSettings.sharedPictures',
  167. 2: 'statusSettings.sharedApplications',
  168. 3: 'statusSettings.sharedMisc'
  169. }
  170. # 获取对应的 key
  171. key = key_map.get(dir_type, 'statusSettings.sharedPictures')
  172. # 处理路径分隔符
  173. path = path.replace("\\", "/")
  174. # 拼接配置文件路径
  175. config_file = os.path.join(self.install_path, 'vms', 'config', f'leidian{index}.config')
  176. if not os.path.exists(config_file):
  177. raise FileNotFoundError(f"Config file not found: {config_file}")
  178. try:
  179. # 读取配置文件
  180. with open(config_file, 'r', encoding='utf-8') as file:
  181. config = json.load(file)
  182. # 设置共享目录
  183. config[key] = path
  184. # 写回配置文件
  185. with open(config_file, 'w', encoding='utf-8') as file: # type: TextIO
  186. json.dump(config, file, ensure_ascii=False, indent=4)
  187. return True
  188. except FileNotFoundError:
  189. raise FileNotFoundError(f"Config file not found: {config_file}")
  190. except json.JSONDecodeError:
  191. raise json.JSONDecodeError(f"Error decoding JSON from {config_file}")
  192. except Exception as e:
  193. raise Exception(f"An unexpected error occurred: {e}")
  194. def get_share_dir(self, index: int, dir_type: int = 1):
  195. key_map = {
  196. 1: 'statusSettings.sharedPictures',
  197. 2: 'statusSettings.sharedApplications',
  198. 3: 'statusSettings.sharedMisc'
  199. }
  200. key = key_map.get(dir_type, 'statusSettings.sharedPictures')
  201. config_file = self.install_path + f'\\vms\\config\\leidian{index}.config'
  202. try:
  203. with open(config_file, 'r', encoding='utf-8') as file:
  204. config = json.load(file)
  205. return True, config[key]
  206. except FileNotFoundError:
  207. raise FileNotFoundError(f"Config file not found: {config_file}")
  208. except json.JSONDecodeError:
  209. raise json.JSONDecodeError(f"Error decoding JSON from {config_file}")
  210. except Exception as e:
  211. raise Exception(f"An unexpected error occurred: {e}")
  212. def start_app(self, index, packagename):
  213. return self.dnc(f"runapp --index {index} --packagename {packagename}")
  214. @staticmethod
  215. def get_ld_install_dir(_type: int) -> str:
  216. """
  217. 获取雷电模拟器的安装路径。
  218. :param _type: 模拟器类型 (4 表示雷电模拟器4, 9 表示雷电模拟器9)
  219. :return: 安装路径字符串,若未找到则返回空字符串
  220. """
  221. install_path = ""
  222. if _type == 4:
  223. install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer", "InstallDir")
  224. if not install_path:
  225. install_path = Utils.get_install_dir_from_shortcut("雷电模拟器4.lnk").replace("dnplayer.exe", "")
  226. if _type == 64:
  227. install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer64", "InstallDir")
  228. if not install_path:
  229. install_path = Utils.get_install_dir_from_shortcut("雷电模拟器64.lnk").replace("dnplayer.exe", "")
  230. elif _type == 9:
  231. install_path = Utils.get_registry_value(r"SOFTWARE\leidian\ldplayer9", "InstallDir")
  232. if not install_path:
  233. install_path = Utils.get_install_dir_from_shortcut("雷电模拟器9.lnk").replace("dnplayer.exe", "")
  234. if not install_path:
  235. raise FileNotFoundError(f"雷电模拟器-{_type}:安装路径未找到")
  236. return install_path
  237. @staticmethod
  238. def get_device_ip_by_index(index: int):
  239. return 'emulator-' + str(5554 + 2 * index)
  240. @staticmethod
  241. def get_index_by_device_ip(device_ip: str):
  242. return (int(device_ip.replace('emulator-', '')) - 5554) // 2