|  | 
| 12 | 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| 13 | 13 | # See the License for the specific language governing permissions and | 
| 14 | 14 | # limitations under the License. | 
| 15 |  | -"""ADB debugging binary. | 
| 16 | 15 | 
 | 
| 17 |  | -Call it similar to how you call android's adb. Takes either --serial or | 
| 18 |  | ---port_path to connect to a device. | 
| 19 |  | -""" | 
|  | 16 | +"""Daemon-less ADB client in python.""" | 
|  | 17 | + | 
|  | 18 | +import argparse | 
|  | 19 | +import functools | 
|  | 20 | +import logging | 
| 20 | 21 | import os | 
|  | 22 | +import stat | 
| 21 | 23 | import sys | 
| 22 |  | - | 
| 23 |  | -import gflags | 
|  | 24 | +import time | 
| 24 | 25 | 
 | 
| 25 | 26 | import adb_commands | 
| 26 | 27 | import common_cli | 
|  | 
| 36 | 37 |     rsa_signer = None | 
| 37 | 38 | 
 | 
| 38 | 39 | 
 | 
| 39 |  | -gflags.ADOPT_module_key_flags(common_cli) | 
|  | 40 | +def Devices(args): | 
|  | 41 | +  """Lists the available devices. | 
|  | 42 | +
 | 
|  | 43 | +  Mimics 'adb devices' output: | 
|  | 44 | +    List of devices attached | 
|  | 45 | +    015DB7591102001A        device        1,2 | 
|  | 46 | +  """ | 
|  | 47 | +  for d in adb_commands.AdbCommands.Devices(): | 
|  | 48 | +    if args.output_port_path: | 
|  | 49 | +      print('%s\tdevice\t%s' % ( | 
|  | 50 | +            d.serial_number, ','.join(str(p) for p in d.port_path))) | 
|  | 51 | +    else: | 
|  | 52 | +      print('%s\tdevice' % d.serial_number) | 
|  | 53 | +  return 0 | 
|  | 54 | + | 
|  | 55 | + | 
|  | 56 | +def List(self, device_path): | 
|  | 57 | +  """Prints a directory listing. | 
|  | 58 | +
 | 
|  | 59 | +  Args: | 
|  | 60 | +    device_path: Directory to list. | 
|  | 61 | +  """ | 
|  | 62 | +  files = adb_commands.AdbCommands.List(self, device_path) | 
|  | 63 | +  files.sort(key=lambda x: x.filename) | 
|  | 64 | +  maxname = max(len(f.filename) for f in files) | 
|  | 65 | +  maxsize = max(len(str(f.size)) for f in files) | 
|  | 66 | +  for f in files: | 
|  | 67 | +    mode = ( | 
|  | 68 | +      ('d' if stat.S_ISDIR(f.mode) else '-') + | 
|  | 69 | +      ('r' if f.mode & stat.S_IRUSR else '-') + | 
|  | 70 | +      ('w' if f.mode & stat.S_IWUSR else '-') + | 
|  | 71 | +      ('x' if f.mode & stat.S_IXUSR else '-') + | 
|  | 72 | +      ('r' if f.mode & stat.S_IRGRP else '-') + | 
|  | 73 | +      ('w' if f.mode & stat.S_IWGRP else '-') + | 
|  | 74 | +      ('x' if f.mode & stat.S_IXGRP else '-') + | 
|  | 75 | +      ('r' if f.mode & stat.S_IROTH else '-') + | 
|  | 76 | +      ('w' if f.mode & stat.S_IWOTH else '-') + | 
|  | 77 | +      ('x' if f.mode & stat.S_IXOTH else '-')) | 
|  | 78 | +    t = time.gmtime(f.mtime) | 
|  | 79 | +    yield '%s %*d %04d-%02d-%02d %02d:%02d:%02d %-*s\n' % ( | 
|  | 80 | +        mode, maxsize, f.size, | 
|  | 81 | +        t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, | 
|  | 82 | +        maxname, f.filename) | 
|  | 83 | + | 
|  | 84 | + | 
|  | 85 | +@functools.wraps(adb_commands.AdbCommands.Logcat) | 
|  | 86 | +def Logcat(self, *options): | 
|  | 87 | +  return adb_commands.AdbCommands.Logcat(self, ' '.join(options)) | 
|  | 88 | + | 
|  | 89 | + | 
|  | 90 | +def Shell(self, *command): | 
|  | 91 | +  """Runs a command on the device and prints the stdout. | 
|  | 92 | +
 | 
|  | 93 | +  Args: | 
|  | 94 | +    command: Command to run on the target. | 
|  | 95 | +  """ | 
|  | 96 | +  return adb_commands.AdbCommands.StreamingShell(self, ' '.join(command)) | 
|  | 97 | + | 
|  | 98 | + | 
|  | 99 | +def main(): | 
|  | 100 | +  common = common_cli.GetCommonArguments() | 
|  | 101 | +  common.add_argument( | 
|  | 102 | +      '--rsa_key_path', action='append', default=[], | 
|  | 103 | +      metavar='~/.android/adbkey', | 
|  | 104 | +      help='RSA key(s) to use, use multiple times to load mulitple keys') | 
|  | 105 | +  common.add_argument( | 
|  | 106 | +    '--auth_timeout_s', default=60., metavar='60', type=int, | 
|  | 107 | +    help='Seconds to wait for the dialog to be accepted when using ' | 
|  | 108 | +         'authenticated ADB.') | 
|  | 109 | +  device = common_cli.GetDeviceArguments() | 
|  | 110 | +  parents = [common, device] | 
|  | 111 | + | 
|  | 112 | +  parser = argparse.ArgumentParser( | 
|  | 113 | +      description=sys.modules[__name__].__doc__, parents=[common]) | 
|  | 114 | +  subparsers = parser.add_subparsers(title='Commands', dest='command_name') | 
| 40 | 115 | 
 | 
| 41 |  | -gflags.DEFINE_multistring('rsa_key_path', '~/.android/adbkey', | 
| 42 |  | -                         'RSA key(s) to use') | 
| 43 |  | -gflags.DEFINE_integer('auth_timeout_s', 60, | 
| 44 |  | -                     'Seconds to wait for the dialog to be accepted when using ' | 
| 45 |  | -                     'authenticated ADB.') | 
| 46 |  | -FLAGS = gflags.FLAGS | 
|  | 116 | +  subparser = subparsers.add_parser( | 
|  | 117 | +      name='help', help='Prints the commands available') | 
|  | 118 | +  subparser = subparsers.add_parser( | 
|  | 119 | +      name='devices', help='Lists the available devices', parents=[common]) | 
|  | 120 | +  subparser.add_argument( | 
|  | 121 | +      '--output_port_path', action='store_true', | 
|  | 122 | +      help='Outputs the port_path alongside the serial') | 
| 47 | 123 | 
 | 
|  | 124 | +  common_cli.MakeSubparser( | 
|  | 125 | +      subparsers, parents, adb_commands.AdbCommands.Install) | 
|  | 126 | +  common_cli.MakeSubparser(subparsers, parents, List) | 
|  | 127 | +  common_cli.MakeSubparser(subparsers, parents, Logcat) | 
|  | 128 | +  common_cli.MakeSubparser( | 
|  | 129 | +      subparsers, parents, adb_commands.AdbCommands.Push, | 
|  | 130 | +      {'source_file': 'Filename or directory to push to the device.'}) | 
|  | 131 | +  common_cli.MakeSubparser( | 
|  | 132 | +      subparsers, parents, adb_commands.AdbCommands.Pull, | 
|  | 133 | +      { | 
|  | 134 | +        'dest_file': 'Filename to write to on the host, if not specified, ' | 
|  | 135 | +                     'prints the content to stdout.', | 
|  | 136 | +      }) | 
|  | 137 | +  common_cli.MakeSubparser( | 
|  | 138 | +      subparsers, parents, adb_commands.AdbCommands.Reboot) | 
|  | 139 | +  common_cli.MakeSubparser( | 
|  | 140 | +      subparsers, parents, adb_commands.AdbCommands.RebootBootloader) | 
|  | 141 | +  common_cli.MakeSubparser( | 
|  | 142 | +      subparsers, parents, adb_commands.AdbCommands.Remount) | 
|  | 143 | +  common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Root) | 
|  | 144 | +  common_cli.MakeSubparser(subparsers, parents, Shell) | 
| 48 | 145 | 
 | 
| 49 |  | -def GetRSAKwargs(): | 
| 50 |  | -  if FLAGS.rsa_key_path: | 
| 51 |  | -    if rsa_signer is None: | 
| 52 |  | -      print >> sys.stderr, 'Please install either M2Crypto or python-rsa' | 
| 53 |  | -      sys.exit(1) | 
| 54 |  | -    return { | 
| 55 |  | -        'rsa_keys': [rsa_signer(os.path.expanduser(path)) | 
| 56 |  | -                     for path in FLAGS.rsa_key_path], | 
| 57 |  | -        'auth_timeout_ms': int(FLAGS.auth_timeout_s * 1000.0), | 
| 58 |  | -    } | 
| 59 |  | -  return {} | 
|  | 146 | +  if len(sys.argv) == 1: | 
|  | 147 | +    parser.print_help() | 
|  | 148 | +    return 2 | 
| 60 | 149 | 
 | 
|  | 150 | +  args = parser.parse_args() | 
|  | 151 | +  if args.verbose: | 
|  | 152 | +    logging.basicConfig(level=logging.DEBUG) | 
|  | 153 | +  if not args.rsa_key_path: | 
|  | 154 | +    default = os.path.expanduser('~/.android/adbkey') | 
|  | 155 | +    if os.path.isfile(default): | 
|  | 156 | +      args.rsa_key_path = [default] | 
|  | 157 | +  if args.rsa_key_path and not rsa_signer: | 
|  | 158 | +    parser.error('Please install either M2Crypto or python-rsa') | 
|  | 159 | +  # Hacks so that the generated doc is nicer. | 
|  | 160 | +  if args.command_name == 'devices': | 
|  | 161 | +    return Devices(args) | 
|  | 162 | +  if args.command_name == 'help': | 
|  | 163 | +    parser.print_help() | 
|  | 164 | +    return 0 | 
|  | 165 | +  if args.command_name == 'logcat': | 
|  | 166 | +    args.positional = args.options | 
|  | 167 | +  elif args.command_name == 'shell': | 
|  | 168 | +    args.positional = args.command | 
| 61 | 169 | 
 | 
| 62 |  | -def main(argv): | 
| 63 |  | -  common_cli.StartCli( | 
| 64 |  | -      argv, adb_commands.AdbCommands.ConnectDevice, | 
| 65 |  | -      list_callback=adb_commands.AdbCommands.Devices, **GetRSAKwargs()) | 
|  | 170 | +  return common_cli.StartCli( | 
|  | 171 | +      args, | 
|  | 172 | +      adb_commands.AdbCommands.ConnectDevice, | 
|  | 173 | +      auth_timeout_ms=args.auth_timeout_s * 1000, | 
|  | 174 | +      rsa_keys=[rsa_signer(path) for path in args.rsa_key_path]) | 
| 66 | 175 | 
 | 
| 67 | 176 | 
 | 
| 68 | 177 | if __name__ == '__main__': | 
| 69 |  | -  main(FLAGS(sys.argv)) | 
|  | 178 | +  sys.exit(main()) | 
0 commit comments