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.

297 lines
9.0 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
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. import shlex
  9. from android_db import AndroidSQLConn
  10. from load_things import loaded
  11. from decode_parcel import decode_parcel
  12. debug = True
  13. config = loaded.config
  14. keycodes = loaded.keycodes
  15. exe = config.defaults.exe
  16. exists = '''if [ -e "{file}" ]; then
  17. if [ -d "{file}" ]; then
  18. echo "directory"
  19. elif [ -f "{file}" ]; then
  20. echo "file"
  21. else
  22. echo "error"
  23. fi
  24. else
  25. echo "na"
  26. fi'''
  27. def merge(src, dst,log = False):
  28. if not os.path.exists(dst):
  29. return False
  30. ok = True
  31. for path, dirs, files in os.walk(src):
  32. relPath = os.path.relpath(path, src)
  33. destPath = os.path.join(dst, relPath)
  34. if not os.path.exists(destPath):
  35. os.makedirs(destPath)
  36. for file in files:
  37. destFile = os.path.join(destPath, file)
  38. if os.path.isfile(destFile):
  39. if log:
  40. print("Skipping existing file: " + os.path.join(relPath, file))
  41. ok = False
  42. continue
  43. srcFile = os.path.join(path, file)
  44. shutil.move(srcFile, destFile)
  45. for path, dirs, files in os.walk(src, False):
  46. if len(files) == 0 and len(dirs) == 0:
  47. os.rmdir(path)
  48. return ok
  49. def _adb(*args,output = "shell"):
  50. '''Output modes:
  51. "out": return output
  52. "shell": print to shell
  53. "buffered": read line by line'''
  54. args = [exe] + list(args)
  55. if output == "out":
  56. return subprocess.check_output(args,shell = False).decode().rstrip('\r\n')
  57. elif output == "shell":
  58. ret = subprocess.call(args,shell = False)
  59. if ret:
  60. raise subprocess.CalledProcessError(ret,args)
  61. elif output == "buffered":
  62. p = subprocess.Popen(args,stdout = subprocess.PIPE)
  63. return p.stdout
  64. def kill_server():
  65. _adb('kill-server')
  66. def start_server():
  67. _adb('start-server')
  68. def get_info():
  69. start_server()
  70. thing = _adb("devices","-l",output="out")
  71. formed = list(filter(bool,thing.split("\r\n")))[1:]
  72. main = {}
  73. for device in formed:
  74. categories = re.split(" +",device)
  75. device_dict = {
  76. "serial":categories[0],
  77. "mode":categories[1]
  78. }
  79. device_dict.update(dict(category.split(":") for category in categories[2:]))
  80. main[categories[0]] = device_dict
  81. return main
  82. class ADBWrapper:
  83. root_mode = False
  84. def connect(ip,port=5555):
  85. if not re.match(r'(\d{1,3}\.){3}\d{1,3}',ip):
  86. raise TypeError("Invalid ip")
  87. if not all(int(n) <= 255 and int(n) >= 0 for n in ip.split('.')):
  88. raise TypeError("Invalid ip")
  89. if not (port >= 0 and port <= 2**16-1):
  90. raise TyperError("Port must be in the range 0-65536")
  91. id = '{}:{}'.format(ip,port)
  92. _adb('connect','{}:{}'.format(ip,port))
  93. dev = Device(id)
  94. dev.tcip = True
  95. return dev
  96. def disconnect(self):
  97. if self.tcip:
  98. _adb('disconnect',self.serial)
  99. def db_connect(self,filepath):
  100. return AndroidSQLConn(self,filepath)
  101. def prim_device():
  102. cont = True
  103. while cont:
  104. try:
  105. d = Device()
  106. cont = False
  107. except IndexError:
  108. time.sleep(1)
  109. return d
  110. def __init__(self,serial=None):
  111. self.tcip = False
  112. if serial:
  113. self.serial = serial
  114. info = get_info()[serial]
  115. else:
  116. serial,info = list(get_info().items())[0]
  117. self.__dict__.update(info)
  118. def adb(self,*args,output="shell"):
  119. args = ['-s',self.serial]+ list(args)
  120. return _adb(*args,output = output)
  121. def shell(self,*args,output="shell"):
  122. args = ('shell',)+args
  123. return self.adb(*args,output=output)
  124. def sudo(self,*args,output="shell"):
  125. if self.mode == 'recovery' or self.root_mode:
  126. return self.shell(*args,output=output)
  127. else:
  128. return self.shell('su','--','--',*args,output=output)
  129. @classmethod
  130. def root(cls):
  131. cls.root_mode = True
  132. _adb('root')
  133. @classmethod
  134. def unroot(cls):
  135. cls.root_mode = False
  136. _adb('unroot')
  137. def reboot(self,mode = None):
  138. if mode:
  139. if mode == "soft":
  140. if self.mode != 'recovery':
  141. pid = self.shell("pidof","zygote",output="out")
  142. return self.sudo("kill",pid,output="shell")
  143. else:
  144. return self.reboot()
  145. else:
  146. self.adb("reboot",mode)
  147. else:
  148. self.adb("reboot")
  149. while True:
  150. infos = get_info()
  151. if len(infos) > 0:
  152. self.__dict__.update(infos[self.serial])
  153. break
  154. time.sleep(1)
  155. class FSActionWrapper(ADBWrapper):
  156. def type(self,file):
  157. e = exists.format(file = file)
  158. res = self.sudo(e,output="out")
  159. return res
  160. def exists(self,file):
  161. return self.type(file) != "na"
  162. def isfile(self,file):
  163. return self.type(file) == 'file'
  164. def isdir(self,file):
  165. return self.type(file) == 'directory'
  166. def delete(self,path):
  167. return self.sudo("rm","-rf",path,output="out")
  168. def copy(self,remote,local,del_duplicates = True,ignore_error=True):
  169. remote_type = self.type(remote)
  170. if remote_type != "na":
  171. if remote_type == "directory" and not remote.endswith('/'):
  172. remote += '/'
  173. merge_flag = False
  174. if os.path.exists(local):
  175. last = os.path.split(local)[-1]
  176. real_dir = local
  177. local = os.path.join(config['local']['temp'],last)
  178. merge_flag = True
  179. try:
  180. self.adb("pull","-a",remote,local)
  181. except subprocess.CalledProcessError as e:
  182. if ignore_error:
  183. pass
  184. else:
  185. raise e
  186. if merge_flag:
  187. merge(local,real_dir)
  188. if os.path.exists(local) and del_duplicates:
  189. shutil.rmtree(local)
  190. else:
  191. print("File not found: {}".format(remote))
  192. def move(self,remote,local,del_duplicates = True,ignore_error=False):
  193. if self.exists(remote):
  194. self.copy(remote,local,del_duplicates = del_duplicates,ignore_error=ignore_error)
  195. self.delete(remote)
  196. else:
  197. print("File not found: {}".format(remote))
  198. def push(self,local,remote):
  199. self.adb('push',local,remote)
  200. class Input(ADBWrapper):
  201. def send_keycode(self,code):
  202. try:
  203. keycode = keycodes[code]
  204. except KeyError:
  205. keycode = str(code)
  206. self.shell("input","keyevent",keycode)
  207. def unlock_phone(self,password):
  208. if self.mode == 'recovery':
  209. return
  210. if not decode_parcel(self.shell('service','call', 'power','12',output="out"),'int'):
  211. self.send_keycode('power')
  212. if decode_parcel(self.shell('service','call','trust','7',output="out"),'int'):
  213. self.send_keycode('space')
  214. self.shell("input","text",str(password))
  215. self.send_keycode('enter')
  216. class TWRP(FSActionWrapper):
  217. def backup(self,*partitions,name = None,backupdir = None):
  218. if self.mode != 'recovery':
  219. self.reboot('recovery')
  220. if backupdir is None:
  221. backupdir = config['local']['twrp']
  222. else:
  223. backupdir = backupdir
  224. options_dict = {
  225. "system": "S",
  226. "data": "D",
  227. "cache": "C",
  228. "recovery": "R",
  229. "spec_part_1": "1",
  230. "spec_part_2": "2",
  231. "spec_part_3": "3",
  232. "boot": "B",
  233. "as": "A"
  234. }
  235. options = "".join(options_dict[option] for option in partitions)
  236. if not name:
  237. name = "backup_"+datetime.datetime.today().strftime(defaults['date_format'])
  238. filename = os.path.join(backupdir,name)
  239. self.shell("twrp","backup",options,name)
  240. phone_dir = "/data/media/0/TWRP/BACKUPS/{serial}/{name}".format(serial = self.serial,name = name)
  241. self.move(phone_dir,filename)
  242. def wipe(self,partition):
  243. if self.mode != 'recovery':
  244. self.reboot('recovery')
  245. self.shell("twrp","wipe",partition)
  246. def install(self,name):
  247. if self.mode != 'recovery':
  248. self.reboot('recovery')
  249. if os.path.exists(name):
  250. local_name = name
  251. name = os.path.split(name)[-1]
  252. update_path = '{}/{}'.format(config['remote']['updates'],name)
  253. if not self.exists(config['remote']['updates']):
  254. self.sudo('mkdir',config['remote']['updates'])
  255. if not self.exists(update_path):
  256. self.push(local_name,config['remote']['updates'])
  257. else:
  258. update_path = '{}/{}'.format(config['remote']['updates'],name)
  259. self.shell("twrp","install",update_path)
  260. class Device(TWRP,Input):
  261. pass
  262. if __name__ == "__main__" and debug:
  263. d = Device.prim_device()