adb_debug.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/usr/bin/env python
  2. # Copyright 2014 Google Inc. All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Daemon-less ADB client in python."""
  16. import argparse
  17. import functools
  18. import logging
  19. import os
  20. import stat
  21. import sys
  22. import time
  23. from adb import adb_commands
  24. from adb import common_cli
  25. try:
  26. from adb import sign_m2crypto
  27. rsa_signer = sign_m2crypto.M2CryptoSigner
  28. except ImportError:
  29. try:
  30. from adb import sign_pythonrsa
  31. rsa_signer = sign_pythonrsa.PythonRSASigner.FromRSAKeyPath
  32. except ImportError:
  33. try:
  34. from adb import sign_pycryptodome
  35. rsa_signer = sign_pycryptodome.PycryptodomeAuthSigner
  36. except ImportError:
  37. rsa_signer = None
  38. def Devices(args):
  39. """Lists the available devices.
  40. Mimics 'adb devices' output:
  41. List of devices attached
  42. 015DB7591102001A device 1,2
  43. """
  44. for d in adb_commands.AdbCommands.Devices():
  45. if args.output_port_path:
  46. print('%s\tdevice\t%s' % (
  47. d.serial_number, ','.join(str(p) for p in d.port_path)))
  48. else:
  49. print('%s\tdevice' % d.serial_number)
  50. return 0
  51. def List(device, device_path):
  52. """Prints a directory listing.
  53. Args:
  54. device_path: Directory to list.
  55. """
  56. files = device.List(device_path)
  57. files.sort(key=lambda x: x.filename)
  58. maxname = max(len(f.filename) for f in files)
  59. maxsize = max(len(str(f.size)) for f in files)
  60. for f in files:
  61. mode = (
  62. ('d' if stat.S_ISDIR(f.mode) else '-') +
  63. ('r' if f.mode & stat.S_IRUSR else '-') +
  64. ('w' if f.mode & stat.S_IWUSR else '-') +
  65. ('x' if f.mode & stat.S_IXUSR else '-') +
  66. ('r' if f.mode & stat.S_IRGRP else '-') +
  67. ('w' if f.mode & stat.S_IWGRP else '-') +
  68. ('x' if f.mode & stat.S_IXGRP else '-') +
  69. ('r' if f.mode & stat.S_IROTH else '-') +
  70. ('w' if f.mode & stat.S_IWOTH else '-') +
  71. ('x' if f.mode & stat.S_IXOTH else '-'))
  72. t = time.gmtime(f.mtime)
  73. yield '%s %*d %04d-%02d-%02d %02d:%02d:%02d %-*s\n' % (
  74. mode, maxsize, f.size,
  75. t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
  76. maxname, f.filename)
  77. @functools.wraps(adb_commands.AdbCommands.Logcat)
  78. def Logcat(device, *options):
  79. return device.Logcat(
  80. device, ' '.join(options), timeout_ms=0)
  81. def Shell(device, *command):
  82. """Runs a command on the device and prints the stdout.
  83. Args:
  84. command: Command to run on the target.
  85. """
  86. if command:
  87. return device.StreamingShell(' '.join(command))
  88. else:
  89. # Retrieve the initial terminal prompt to use as a delimiter for future reads
  90. terminal_prompt = device.InteractiveShell()
  91. print(terminal_prompt.decode('utf-8'))
  92. # Accept user input in a loop and write that into the interactive shells stdin, then print output
  93. while True:
  94. cmd = input('> ')
  95. if not cmd:
  96. continue
  97. elif cmd == 'exit':
  98. break
  99. else:
  100. stdout = device.InteractiveShell(cmd, strip_cmd=True, delim=terminal_prompt, strip_delim=True)
  101. if stdout:
  102. if isinstance(stdout, bytes):
  103. stdout = stdout.decode('utf-8')
  104. print(stdout)
  105. device.Close()
  106. def main():
  107. common = common_cli.GetCommonArguments()
  108. common.add_argument(
  109. '--rsa_key_path', action='append', default=[],
  110. metavar='~/.android/adbkey',
  111. help='RSA key(s) to use, use multiple times to load mulitple keys')
  112. common.add_argument(
  113. '--auth_timeout_s', default=60., metavar='60', type=int,
  114. help='Seconds to wait for the dialog to be accepted when using '
  115. 'authenticated ADB.')
  116. device = common_cli.GetDeviceArguments()
  117. parents = [common, device]
  118. parser = argparse.ArgumentParser(
  119. description=sys.modules[__name__].__doc__, parents=[common])
  120. subparsers = parser.add_subparsers(title='Commands', dest='command_name')
  121. subparser = subparsers.add_parser(
  122. name='help', help='Prints the commands available')
  123. subparser = subparsers.add_parser(
  124. name='devices', help='Lists the available devices', parents=[common])
  125. subparser.add_argument(
  126. '--output_port_path', action='store_true',
  127. help='Outputs the port_path alongside the serial')
  128. common_cli.MakeSubparser(
  129. subparsers, parents, adb_commands.AdbCommands.Install)
  130. common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Uninstall)
  131. common_cli.MakeSubparser(subparsers, parents, List)
  132. common_cli.MakeSubparser(subparsers, parents, Logcat)
  133. common_cli.MakeSubparser(
  134. subparsers, parents, adb_commands.AdbCommands.Push,
  135. {'source_file': 'Filename or directory to push to the device.'})
  136. common_cli.MakeSubparser(
  137. subparsers, parents, adb_commands.AdbCommands.Pull,
  138. {
  139. 'dest_file': 'Filename to write to on the host, if not specified, '
  140. 'prints the content to stdout.',
  141. })
  142. common_cli.MakeSubparser(
  143. subparsers, parents, adb_commands.AdbCommands.Reboot)
  144. common_cli.MakeSubparser(
  145. subparsers, parents, adb_commands.AdbCommands.RebootBootloader)
  146. common_cli.MakeSubparser(
  147. subparsers, parents, adb_commands.AdbCommands.Remount)
  148. common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Root)
  149. common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.EnableVerity)
  150. common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.DisableVerity)
  151. common_cli.MakeSubparser(subparsers, parents, Shell)
  152. if len(sys.argv) == 1:
  153. parser.print_help()
  154. return 2
  155. args = parser.parse_args()
  156. if args.verbose:
  157. logging.basicConfig(level=logging.DEBUG)
  158. if not args.rsa_key_path:
  159. default = os.path.expanduser('~/.android/adbkey')
  160. if os.path.isfile(default):
  161. args.rsa_key_path = [default]
  162. if args.rsa_key_path and not rsa_signer:
  163. parser.error('Please install either M2Crypto, python-rsa, or PycryptoDome')
  164. # Hacks so that the generated doc is nicer.
  165. if args.command_name == 'devices':
  166. return Devices(args)
  167. if args.command_name == 'help':
  168. parser.print_help()
  169. return 0
  170. if args.command_name == 'logcat':
  171. args.positional = args.options
  172. elif args.command_name == 'shell':
  173. args.positional = args.command
  174. return common_cli.StartCli(
  175. args,
  176. adb_commands.AdbCommands,
  177. auth_timeout_ms=int(args.auth_timeout_s * 1000),
  178. rsa_keys=[rsa_signer(path) for path in args.rsa_key_path])
  179. if __name__ == '__main__':
  180. sys.exit(main())