You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
8.3 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. import datetime
  2. import json
  3. import os
  4. import re
  5. import shutil
  6. import subprocess
  7. import time
  8. debug = True
  9. path = os.path.dirname(__file__)
  10. with open(os.path.join(path,'defaults.json')) as d,open(os.path.join(path,'keycodes.json')) as k:
  11. defaults = json.load(d)
  12. keycodes = json.load(k)
  13. exe = defaults['exe']
  14. for key,value in defaults['local'].items():
  15. defaults['local'][key] = os.path.expandvars(value)
  16. def decode_parcel(s):
  17. bytes_pat = re.compile(r'(?<!0x)[a-f0-9]{8}')
  18. res = ''
  19. bytes_str = ''.join(match.group(0) for match in bytes_pat.finditer(s))
  20. for p in range(0,len(bytes_str),2):
  21. c= chr(int(bytes_str[p:p+2],16))
  22. res += c
  23. if '$' in res:
  24. res = re.search(r'(?<=\$).+',res).group(0)
  25. fres = ''
  26. for c in range(0,len(res),4):
  27. a = res[c:c+2]
  28. b = res[c+2:c+4]
  29. fres += b+a
  30. return fres
  31. else:
  32. try:
  33. return int(bytes_str,16)
  34. except:
  35. print(bytes_str,s)
  36. def merge(src, dst,log = False):
  37. if not os.path.exists(dst):
  38. return False
  39. ok = True
  40. for path, dirs, files in os.walk(src):
  41. relPath = os.path.relpath(path, src)
  42. destPath = os.path.join(dst, relPath)
  43. if not os.path.exists(destPath):
  44. os.makedirs(destPath)
  45. for file in files:
  46. destFile = os.path.join(destPath, file)
  47. if os.path.isfile(destFile):
  48. if log:
  49. print("Skipping existing file: " + os.path.join(relPath, file))
  50. ok = False
  51. continue
  52. srcFile = os.path.join(path, file)
  53. shutil.move(srcFile, destFile)
  54. for path, dirs, files in os.walk(src, False):
  55. if len(files) == 0 and len(dirs) == 0:
  56. os.rmdir(path)
  57. return ok
  58. def _adb(*args,out = False):
  59. args = [exe] + list(args)
  60. if out:
  61. return subprocess.check_output(args,shell = False).decode().rstrip('\r\n')
  62. else:
  63. ret = subprocess.call(args,shell = False)
  64. if ret:
  65. raise subprocess.CalledProcessError(ret,args)
  66. def get_info():
  67. thing = _adb("devices","-l",out = True)
  68. formed = list(filter(bool,thing.split("\r\n")))[1:]
  69. main = {}
  70. for device in formed:
  71. categories = re.split(" +",device)
  72. device_dict = {
  73. "serial":categories[0],
  74. "mode":categories[1]
  75. }
  76. device_dict.update(dict(category.split(":") for category in categories[2:]))
  77. main[categories[0]] = device_dict
  78. return main
  79. class device:
  80. #init operations
  81. def prim_device():
  82. cont = True
  83. while cont:
  84. try:
  85. d = device()
  86. cont = False
  87. except IndexError:
  88. time.sleep(1)
  89. return d
  90. #todo connect over tcip
  91. def __init__(self,serial=None):
  92. if serial:
  93. self.serial = serial
  94. info = get_info()[serial]
  95. else:
  96. serial,info = list(get_info().items())[0]
  97. self.__dict__.update(info)
  98. #end of init operations
  99. #command interface
  100. def adb(self,*args,out = False):
  101. args = ['-s',self.serial]+ list(args)
  102. return _adb(*args,out = out)
  103. def shell(self,*args,out=False):
  104. args = ('shell',)+args
  105. return self.adb(*args,out=out)
  106. def sudo(self,*args,out = False):
  107. if self.mode == 'recovery':
  108. return self.shell(*args,out=out)
  109. else:
  110. args = '"{}"'.format(' '.join(args).replace('"','\\"'))
  111. return self.shell('su','-c',args,out = out)
  112. #end of cammand interface
  113. #file operations
  114. def type(self,file):
  115. exists = '''if [ -e "{file}" ]; then
  116. if [ -d "{file}" ]; then
  117. echo "directory"
  118. elif [ -f "{file}" ]; then
  119. echo "file"
  120. else
  121. echo "error"
  122. fi
  123. else
  124. echo "na"
  125. fi'''
  126. e = exists.format(file = file)
  127. res = self.sudo(e,out=True)
  128. return res
  129. def exists(self,file):
  130. return self.type(file) != "na"
  131. def isfile(self,file):
  132. return self.type(file) == 'file'
  133. def isdir(self,file):
  134. return self.type(file) == 'directory'
  135. def delete(self,path):
  136. return self.sudo("rm","-rf",path,out=True)
  137. def copy(self,remote,local,del_duplicates = True,ignore_error=True):
  138. remote_type = self.type(remote)
  139. if remote_type != "na":
  140. if remote_type == "directory" and not remote.endswith('/'):
  141. remote += '/'
  142. merge_flag = False
  143. if os.path.exists(local):
  144. last = os.path.split(local)[-1]
  145. real_dir = local
  146. local = os.path.join(defaults['local']['temp'],last)
  147. merge_flag = True
  148. try:
  149. self.adb("pull","-a",remote,local)
  150. except subprocess.CalledProcessError as e:
  151. if ignore_error:
  152. pass
  153. else:
  154. raise e
  155. if merge_flag:
  156. merge(local,real_dir)
  157. if os.path.exists(local) and del_duplicates:
  158. shutil.rmtree(local)
  159. else:
  160. print("File not found: {}".format(remote))
  161. def move(self,remote,local,del_duplicates = True,ignore_error=False):
  162. if self.exists(remote):
  163. self.copy(remote,local,del_duplicates = del_duplicates,ignore_error=ignore_error)
  164. self.delete(remote)
  165. else:
  166. print("File not found: {}".format(remote))
  167. def push(self,local,remote):
  168. self.adb('push',local,remote)
  169. #end of file operations
  170. #convenience
  171. def reboot(self,mode = None):
  172. if mode:
  173. if mode == "soft":
  174. if self.mode != 'recovery':
  175. pid = self.shell("pidof","zygote",out = True)
  176. return self.sudo("kill",pid,out=False)
  177. else:
  178. return self.reboot()
  179. else:
  180. self.adb("reboot",mode)
  181. else:
  182. self.adb("reboot")
  183. while True:
  184. infos = get_info()
  185. if len(infos) > 0:
  186. self.__dict__.update(infos[self.serial])
  187. break
  188. time.sleep(1)
  189. def send_keycode(self,code):
  190. try:
  191. keycode = keycodes[code]
  192. except KeyError:
  193. keycode = str(code)
  194. self.shell("input","keyevent",keycode)
  195. def unlock_phone(self,pin):
  196. if self.mode != 'recovery':
  197. if not decode_parcel(self.shell('service','call', 'power','12',out=True)):
  198. self.send_keycode('power')
  199. if decode_parcel(self.shell('service','call','trust','7',out=True)):
  200. self.send_keycode('space')
  201. self.shell("input","text",str(pin))
  202. self.send_keycode('enter')
  203. #end of convenience
  204. #twrp
  205. def backup(self,*partitions,name = None):
  206. if self.mode != 'recovery':
  207. self.reboot('recovery')
  208. backupdir = defaults['local']['TWRP']
  209. options_dict = {
  210. "system": "S",
  211. "data": "D",
  212. "cache": "C",
  213. "recovery": "R",
  214. "spec_part_1": "1",
  215. "spec_part_2": "2",
  216. "spec_part_3": "3",
  217. "boot": "B",
  218. "as": "A"
  219. }
  220. options = "".join(options_dict[option] for option in partitions)
  221. if not name:
  222. name = "backup_"+datetime.datetime.today().strftime(defaults['date_format'])
  223. filename = os.path.join(backupdir,name)
  224. self.shell("twrp","backup",options,name)
  225. phone_dir = "/data/media/0/TWRP/BACKUPS/{serial}/{name}".format(serial = self.serial,name = name)
  226. self.move(phone_dir,filename)
  227. def wipe(self,partition):
  228. if self.mode != 'recovery':
  229. self.reboot('recovery')
  230. self.shell("twrp","wipe",partition)
  231. def install(self,name):
  232. if self.mode != 'recovery':
  233. self.reboot('recovery')
  234. if os.path.exists(name):
  235. local_name = name
  236. name = os.path.split(name)[-1]
  237. update_path = '{}/{}'.format(defaults['remote']['updates'],name)
  238. if not self.exists(update_path):
  239. self.push(local_name,defaults['remote']['updates'])
  240. else:
  241. update_path = '{}/{}'.format(defaults['remote']['updates'],name)
  242. self.shell("twrp","install",update_path)
  243. #end of twrp
  244. if __name__ == "__main__" and debug:
  245. d = device.prim_device()