Source code for adb.adb_debug

#!/usr/bin/env python
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Daemon-less ADB client in python.


.. rubric:: Contents

* :func:`Devices`
* :func:`List`
* :func:`Logcat`
* :func:`Shell`

"""

import argparse
import functools
import logging
import os
import stat
import sys
import time

from adb import adb_commands
from adb import common_cli

try:
    from adb import sign_cryptography

    rsa_signer = sign_cryptography.CryptographySigner
except ImportError:
    try:
        from adb import sign_pythonrsa

        rsa_signer = sign_pythonrsa.PythonRSASigner.FromRSAKeyPath
    except ImportError:
        try:
            from adb import sign_pycryptodome

            rsa_signer = sign_pycryptodome.PycryptodomeAuthSigner
        except ImportError:
            rsa_signer = None


[docs]def Devices(args): """Lists the available devices. Mimics ``adb devices`` output: :: List of devices attached 015DB7591102001A device 1,2 .. seealso:: :meth:`adb.adb_commands.AdbCommands.Devices` .. image:: _static/adb.adb_debug.Devices.CALLER_GRAPH.svg Parameters ---------- args : argparse.ArgumentParser CLI arguments; see :func:`adb.common_cli.GetDeviceArguments`. Returns ------- int 0 """ for d in adb_commands.AdbCommands.Devices(): if args.output_port_path: print('%s\tdevice\t%s' % (d.serial_number, ','.join(str(p) for p in d.port_path))) else: print('%s\tdevice' % d.serial_number) return 0
[docs]def List(device, device_path): """Prints a directory listing. .. seealso:: :meth:`adb.adb_commands.AdbCommands.List` Parameters ---------- device : adb.adb_commands.AdbCommands TODO device_path : str, bytes Directory to list. Yields ------ str A formatted listing for a file in ``device_path`` """ files = device.List(device_path) files.sort(key=lambda x: x.filename) maxname = max(len(f.filename) for f in files) maxsize = max(len(str(f.size)) for f in files) for f in files: mode = (('d' if stat.S_ISDIR(f.mode) else '-') + ('r' if f.mode & stat.S_IRUSR else '-') + ('w' if f.mode & stat.S_IWUSR else '-') + ('x' if f.mode & stat.S_IXUSR else '-') + ('r' if f.mode & stat.S_IRGRP else '-') + ('w' if f.mode & stat.S_IWGRP else '-') + ('x' if f.mode & stat.S_IXGRP else '-') + ('r' if f.mode & stat.S_IROTH else '-') + ('w' if f.mode & stat.S_IWOTH else '-') + ('x' if f.mode & stat.S_IXOTH else '-')) t = time.gmtime(f.mtime) yield '%s %*d %04d-%02d-%02d %02d:%02d:%02d %-*s\n' % ( mode, maxsize, f.size, t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, maxname, f.filename)
@functools.wraps(adb_commands.AdbCommands.Logcat) def Logcat(device, *options): """Run ``shell logcat`` and stream the output to stdout. .. seealso:: :meth:`adb.adb_commands.AdbCommands.Logcat` Parameters ---------- device : adb.adb_commands.AdbCommands TODO options : list[str] Arguments to pass to ``logcat`` Returns ------- generator The responses from the ``logcat`` command """ return device.Logcat(device, ' '.join(options), timeout_ms=0)
[docs]def Shell(device, *command): """Runs a command on the device and prints the stdout. .. seealso:: :meth:`adb.adb_commands.AdbCommands.StreamingShell`, :meth:`adb.adb_commands.AdbCommands.InteractiveShell` Parameters ---------- device : adb.adb_commands.AdbCommands TODO command : list[str] Command to run on the target. """ if command: return device.StreamingShell(' '.join(command)) # Retrieve the initial terminal prompt to use as a delimiter for future reads terminal_prompt = device.InteractiveShell() print(terminal_prompt.decode('utf-8')) # Accept user input in a loop and write that into the interactive shells stdin, then print output while True: cmd = input('> ') if not cmd: continue if cmd == 'exit': break stdout = device.InteractiveShell(cmd, strip_cmd=True, delim=terminal_prompt, strip_delim=True) if stdout: if isinstance(stdout, bytes): stdout = stdout.decode('utf-8') print(stdout) device.Close()
[docs]def main(): """TODO""" common = common_cli.GetCommonArguments() common.add_argument('--rsa_key_path', action='append', default=[], metavar='~/.android/adbkey', help='RSA key(s) to use, use multiple times to load mulitple keys') common.add_argument('--auth_timeout_s', default=60., metavar='60', type=int, help='Seconds to wait for the dialog to be accepted when using authenticated ADB.') device = common_cli.GetDeviceArguments() parents = [common, device] parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__, parents=[common]) subparsers = parser.add_subparsers(title='Commands', dest='command_name') subparser = subparsers.add_parser(name='help', help='Prints the commands available') subparser = subparsers.add_parser(name='devices', help='Lists the available devices', parents=[common]) subparser.add_argument('--output_port_path', action='store_true', help='Outputs the port_path alongside the serial') common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Install) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Uninstall) common_cli.MakeSubparser(subparsers, parents, List) common_cli.MakeSubparser(subparsers, parents, Logcat) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Push, {'source_file': 'Filename or directory to push to the device.'}) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Pull, {'dest_file': 'Filename to write to on the host, if not specified, prints the content to stdout.'}) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Reboot) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.RebootBootloader) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Remount) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.Root) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.EnableVerity) common_cli.MakeSubparser(subparsers, parents, adb_commands.AdbCommands.DisableVerity) common_cli.MakeSubparser(subparsers, parents, Shell) if len(sys.argv) == 1: parser.print_help() return 2 args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) if not args.rsa_key_path: default = os.path.expanduser('~/.android/adbkey') if os.path.isfile(default): args.rsa_key_path = [default] if args.rsa_key_path and not rsa_signer: parser.error('Please install either cryptography, python-rsa, or PycryptoDome') # Hacks so that the generated doc is nicer. if args.command_name == 'devices': return Devices(args) if args.command_name == 'help': parser.print_help() return 0 if args.command_name == 'logcat': args.positional = args.options elif args.command_name == 'shell': args.positional = args.command return common_cli.StartCli(args, adb_commands.AdbCommands, auth_timeout_ms=int(args.auth_timeout_s * 1000), rsa_keys=[rsa_signer(path) for path in args.rsa_key_path])
if __name__ == '__main__': sys.exit(main())