Advertisement
caffeinatedmike

adb_commands

Nov 12th, 2018
233
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.34 KB | None | 0 0
  1. # Copyright 2014 Google Inc. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. #     http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """A libusb1-based ADB reimplementation.
  15.  
  16. ADB was giving us trouble with its client/server architecture, which is great
  17. for users and developers, but not so great for reliable scripting. This will
  18. allow us to more easily catch errors as Python exceptions instead of checking
  19. random exit codes, and all the other great benefits from not going through
  20. subprocess and a network socket.
  21.  
  22. All timeouts are in milliseconds.
  23. """
  24.  
  25. import io
  26. import os
  27. import socket
  28. import posixpath
  29.  
  30. from adb import adb_protocol
  31. from adb import common
  32. from adb import filesync_protocol
  33.  
  34. # For debugging
  35. import logging
  36. logger = logging.getLogger(__name__)
  37. logger.info("adb_commands.py's logger setup complete.")
  38.  
  39. # From adb.h
  40. CLASS = 0xFF
  41. SUBCLASS = 0x42
  42. PROTOCOL = 0x01
  43. # pylint: disable=invalid-name
  44. DeviceIsAvailable = common.InterfaceMatcher(CLASS, SUBCLASS, PROTOCOL)
  45.  
  46. try:
  47.     # Imported locally to keep compatibility with previous code.
  48.     from adb.sign_m2crypto import M2CryptoSigner
  49. except ImportError:
  50.     # Ignore this error when M2Crypto is not installed, there are other options.
  51.     pass
  52.  
  53.  
  54. class AdbCommands(object):
  55.     """Exposes adb-like methods for use.
  56.  
  57.    Some methods are more-pythonic and/or have more options.
  58.    """
  59.     protocol_handler = adb_protocol.AdbMessage
  60.     filesync_handler = filesync_protocol.FilesyncProtocol
  61.  
  62.     def __init__(self):
  63.  
  64.         self.__reset()
  65.  
  66.     def __reset(self):
  67.         self.build_props = None
  68.         self._handle = None
  69.         self._device_state = None
  70.  
  71.         # Connection table tracks each open AdbConnection objects per service type for program functions
  72.         # that choose to persist an AdbConnection object for their functionality, using
  73.         # self._get_service_connection
  74.         self._service_connections = {}
  75.  
  76.     def _get_service_connection(self, service, service_command=None, create=True, timeout_ms=None):
  77.         """
  78.        Based on the service, get the AdbConnection for that service or create one if it doesnt exist
  79.  
  80.        :param service:
  81.        :param service_command: Additional service parameters to append
  82.        :param create: If False, dont create a connection if it does not exist
  83.        :return:
  84.        """
  85.  
  86.         connection = self._service_connections.get(service, None)
  87.  
  88.         if connection:
  89.             return connection
  90.  
  91.         if not connection and not create:
  92.             return None
  93.  
  94.         if service_command:
  95.             destination_str = b'%s:%s' % (service, service_command)
  96.         else:
  97.             destination_str = service
  98.  
  99.         connection = self.protocol_handler.Open(
  100.             self._handle, destination=destination_str, timeout_ms=timeout_ms)
  101.  
  102.         self._service_connections.update({service: connection})
  103.  
  104.         return connection
  105.  
  106.     def ConnectDevice(self, port_path=None, serial=None, default_timeout_ms=None, **kwargs):
  107.         """Convenience function to setup a transport handle for the adb device from
  108.             usb path or serial then connect to it.
  109.  
  110.        Args:
  111.          port_path: The filename of usb port to use.
  112.          serial: The serial number of the device to use.
  113.          default_timeout_ms: The default timeout in milliseconds to use.
  114.          kwargs: handle: Device handle to use (instance of common.TcpHandle or common.UsbHandle)
  115.                  banner: Connection banner to pass to the remote device
  116.                  rsa_keys: List of AuthSigner subclass instances to be used for
  117.                      authentication. The device can either accept one of these via the Sign
  118.                      method, or we will send the result of GetPublicKey from the first one
  119.                      if the device doesn't accept any of them.
  120.                  auth_timeout_ms: Timeout to wait for when sending a new public key. This
  121.                      is only relevant when we send a new public key. The device shows a
  122.                      dialog and this timeout is how long to wait for that dialog. If used
  123.                      in automation, this should be low to catch such a case as a failure
  124.                      quickly; while in interactive settings it should be high to allow
  125.                      users to accept the dialog. We default to automation here, so it's low
  126.                      by default.
  127.  
  128.        If serial specifies a TCP address:port, then a TCP connection is
  129.        used instead of a USB connection.
  130.        """
  131.  
  132.         # If there isnt a handle override (used by tests), build one here
  133.         if 'handle' in kwargs:
  134.             self._handle = kwargs.pop('handle')
  135.         else:
  136.             # if necessary, convert serial to a unicode string
  137.             if isinstance(serial, (bytes, bytearray)):
  138.                 serial = serial.decode('utf-8')
  139.  
  140.             if serial and ':' in serial:
  141.                 self._handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms)
  142.             else:
  143.                 self._handle = common.UsbHandle.FindAndOpen(
  144.                     DeviceIsAvailable, port_path=port_path, serial=serial,
  145.                     timeout_ms=default_timeout_ms)
  146.  
  147.         self._Connect(**kwargs)
  148.  
  149.         return self
  150.  
  151.     def Close(self):
  152.         for conn in list(self._service_connections.values()):
  153.             if conn:
  154.                 try:
  155.                     conn.Close()
  156.                 except:
  157.                     pass
  158.  
  159.         if self._handle:
  160.             self._handle.Close()
  161.  
  162.         self.__reset()
  163.  
  164.     def _Connect(self, banner=None, **kwargs):
  165.         """Connect to the device.
  166.  
  167.        Args:
  168.          banner: See protocol_handler.Connect.
  169.          **kwargs: See protocol_handler.Connect and adb_commands.ConnectDevice for kwargs.
  170.               Includes handle, rsa_keys, and auth_timeout_ms.
  171.        Returns:
  172.          An instance of this class if the device connected successfully.
  173.        """
  174.  
  175.         if not banner:
  176.             banner = socket.gethostname().encode()
  177.  
  178.         conn_str = self.protocol_handler.Connect(self._handle, banner=banner, **kwargs)
  179.  
  180.         # Remove banner and colons after device state (state::banner)
  181.         parts = conn_str.split(b'::')
  182.         self._device_state = parts[0]
  183.  
  184.         # Break out the build prop info
  185.         self.build_props = str(parts[1].split(b';'))
  186.  
  187.         return True
  188.  
  189.     @classmethod
  190.     def Devices(cls):
  191.         """Get a generator of UsbHandle for devices available."""
  192.         return common.UsbHandle.FindDevices(DeviceIsAvailable)
  193.  
  194.     def GetState(self):
  195.         return self._device_state
  196.  
  197.     def Install(self, apk_path, destination_dir='', replace_existing=True,
  198.                 grant_permissions=False, timeout_ms=None, transfer_progress_callback=None):
  199.         """Install an apk to the device.
  200.  
  201.        Doesn't support verifier file, instead allows destination directory to be
  202.        overridden.
  203.  
  204.        Args:
  205.          apk_path: Local path to apk to install.
  206.          destination_dir: Optional destination directory. Use /system/app/ for
  207.            persistent applications.
  208.          replace_existing: whether to replace existing application
  209.          grant_permissions: If True, grant all permissions to the app specified in its manifest
  210.          timeout_ms: Expected timeout for pushing and installing.
  211.          transfer_progress_callback: callback method that accepts filename, bytes_written and total_bytes of APK transfer
  212.  
  213.        Returns:
  214.          The pm install output.
  215.        """
  216.         if not destination_dir:
  217.             destination_dir = '/data/local/tmp/'
  218.         basename = os.path.basename(apk_path)
  219.         destination_path = posixpath.join(destination_dir, basename)
  220.         self.Push(apk_path, destination_path, timeout_ms=timeout_ms, progress_callback=transfer_progress_callback)
  221.  
  222.         cmd = ['pm install']
  223.         if grant_permissions:
  224.             cmd.append('-g')
  225.         if replace_existing:
  226.             cmd.append('-r')
  227.         cmd.append('"{}"'.format(destination_path))
  228.  
  229.         ret = self.Shell(' '.join(cmd), timeout_ms=timeout_ms)
  230.  
  231.         # Remove the apk
  232.         rm_cmd = ['rm', destination_path]
  233.         rmret = self.Shell(' '.join(rm_cmd), timeout_ms=timeout_ms)
  234.  
  235.         return ret
  236.  
  237.     def Uninstall(self, package_name, keep_data=False, timeout_ms=None):
  238.         """Removes a package from the device.
  239.  
  240.        Args:
  241.          package_name: Package name of target package.
  242.          keep_data: whether to keep the data and cache directories
  243.          timeout_ms: Expected timeout for pushing and installing.
  244.  
  245.        Returns:
  246.          The pm uninstall output.
  247.        """
  248.         cmd = ['pm uninstall']
  249.         if keep_data:
  250.             cmd.append('-k')
  251.         cmd.append('"%s"' % package_name)
  252.  
  253.         return self.Shell(' '.join(cmd), timeout_ms=timeout_ms)
  254.  
  255.     def Push(self, source_file, device_filename, mtime='0', timeout_ms=None, progress_callback=None, st_mode=None):
  256.         """Push a file or directory to the device.
  257.  
  258.        Args:
  259.          source_file: Either a filename, a directory or file-like object to push to
  260.                       the device.
  261.          device_filename: Destination on the device to write to.
  262.          mtime: Optional, modification time to set on the file.
  263.          timeout_ms: Expected timeout for any part of the push.
  264.          st_mode: stat mode for filename
  265.          progress_callback: callback method that accepts filename, bytes_written and total_bytes,
  266.                             total_bytes will be -1 for file-like objects
  267.        """
  268.  
  269.         if isinstance(source_file, str):
  270.             if os.path.isdir(source_file):
  271.                 self.Shell("mkdir " + device_filename)
  272.                 for f in os.listdir(source_file):
  273.                     self.Push(os.path.join(source_file, f), device_filename + '/' + f,
  274.                               progress_callback=progress_callback)
  275.                 return
  276.             source_file = open(source_file, "rb")
  277.  
  278.         with source_file:
  279.             connection = self.protocol_handler.Open(
  280.                 self._handle, destination=b'sync:', timeout_ms=timeout_ms)
  281.             kwargs={}
  282.             if st_mode is not None:
  283.                 kwargs['st_mode'] = st_mode
  284.             self.filesync_handler.Push(connection, source_file, device_filename,
  285.                                        mtime=int(mtime), progress_callback=progress_callback, **kwargs)
  286.         connection.Close()
  287.  
  288.     def Pull(self, device_filename, dest_file=None, timeout_ms=None, progress_callback=None):
  289.         """Pull a file from the device.
  290.  
  291.        Args:
  292.          device_filename: Filename on the device to pull.
  293.          dest_file: If set, a filename or writable file-like object.
  294.          timeout_ms: Expected timeout for any part of the pull.
  295.          progress_callback: callback method that accepts filename, bytes_written and total_bytes,
  296.                             total_bytes will be -1 for file-like objects
  297.  
  298.        Returns:
  299.          The file data if dest_file is not set. Otherwise, True if the destination file exists
  300.        """
  301.         if not dest_file:
  302.             dest_file = io.BytesIO()
  303.         elif isinstance(dest_file, str):
  304.             dest_file = open(dest_file, 'wb')
  305.         elif isinstance(dest_file, file):
  306.             pass
  307.         else:
  308.             raise ValueError("destfile is of unknown type")
  309.  
  310.         conn = self.protocol_handler.Open(
  311.             self._handle, destination=b'sync:', timeout_ms=timeout_ms)
  312.  
  313.         self.filesync_handler.Pull(conn, device_filename, dest_file, progress_callback)
  314.  
  315.         conn.Close()
  316.         if isinstance(dest_file, io.BytesIO):
  317.             return dest_file.getvalue()
  318.         else:
  319.             dest_file.close()
  320.             if hasattr(dest_file, 'name'):
  321.                 return os.path.exists(dest_file.name)
  322.             # We don't know what the path is, so we just assume it exists.
  323.             return True
  324.  
  325.     def Stat(self, device_filename):
  326.         """Get a file's stat() information."""
  327.         connection = self.protocol_handler.Open(self._handle, destination=b'sync:')
  328.         mode, size, mtime = self.filesync_handler.Stat(
  329.             connection, device_filename)
  330.         connection.Close()
  331.         return mode, size, mtime
  332.  
  333.     def List(self, device_path):
  334.         """Return a directory listing of the given path.
  335.  
  336.        Args:
  337.          device_path: Directory to list.
  338.        """
  339.         connection = self.protocol_handler.Open(self._handle, destination=b'sync:')
  340.         listing = self.filesync_handler.List(connection, device_path)
  341.         connection.Close()
  342.         return listing
  343.  
  344.     def Reboot(self, destination=b''):
  345.         """Reboot the device.
  346.  
  347.        Args:
  348.          destination: Specify 'bootloader' for fastboot.
  349.        """
  350.         self.protocol_handler.Open(self._handle, b'reboot:%s' % destination)
  351.  
  352.     def RebootBootloader(self):
  353.         """Reboot device into fastboot."""
  354.         self.Reboot(b'bootloader')
  355.  
  356.     def Remount(self):
  357.         """Remount / as read-write."""
  358.         return self.protocol_handler.Command(self._handle, service=b'remount')
  359.  
  360.     def Root(self):
  361.         """Restart adbd as root on the device."""
  362.         return self.protocol_handler.Command(self._handle, service=b'root')
  363.  
  364.     def EnableVerity(self):
  365.         """Re-enable dm-verity checking on userdebug builds"""
  366.         return self.protocol_handler.Command(self._handle, service=b'enable-verity')
  367.  
  368.     def DisableVerity(self):
  369.         """Disable dm-verity checking on userdebug builds"""
  370.         return self.protocol_handler.Command(self._handle, service=b'disable-verity')
  371.  
  372.     def Shell(self, command, timeout_ms=None):
  373.         """Run command on the device, returning the output.
  374.        Args:
  375.          command: Shell command to run
  376.          timeout_ms: Maximum time to allow the command to run.
  377.        """
  378.         logger.info('Now inside def Shell(self, command, timeout_ms=None) (line 378)')
  379.         logger.info('command: %s', str(command))
  380.         logger.info('timeout_ms: %s', str(timeout_ms))
  381.         return self.protocol_handler.Command(
  382.             self._handle, service=b'shell', command=command,
  383.             timeout_ms=timeout_ms)
  384.  
  385.     def StreamingShell(self, command, timeout_ms=None):
  386.         """Run command on the device, yielding each line of output.
  387.  
  388.        Args:
  389.          command: Command to run on the target.
  390.          timeout_ms: Maximum time to allow the command to run.
  391.  
  392.        Yields:
  393.          The responses from the shell command.
  394.        """
  395.         return self.protocol_handler.StreamingCommand(
  396.             self._handle, service=b'shell', command=command,
  397.             timeout_ms=timeout_ms)
  398.  
  399.     def Logcat(self, options, timeout_ms=None):
  400.         """Run 'shell logcat' and stream the output to stdout.
  401.  
  402.        Args:
  403.          options: Arguments to pass to 'logcat'.
  404.          timeout_ms: Maximum time to allow the command to run.
  405.        """
  406.         return self.StreamingShell('logcat %s' % options, timeout_ms)
  407.  
  408.     def InteractiveShell(self, cmd=None, strip_cmd=True, delim=None, strip_delim=True):
  409.         """Get stdout from the currently open interactive shell and optionally run a command
  410.            on the device, returning all output.
  411.  
  412.        Args:
  413.          cmd: Optional. Command to run on the target.
  414.          strip_cmd: Optional (default True). Strip command name from stdout.
  415.          delim: Optional. Delimiter to look for in the output to know when to stop expecting more output
  416.          (usually the shell prompt)
  417.          strip_delim: Optional (default True): Strip the provided delimiter from the output
  418.  
  419.        Returns:
  420.          The stdout from the shell command.
  421.        """
  422.         conn = self._get_service_connection(b'shell:')
  423.  
  424.         return self.protocol_handler.InteractiveShellCommand(
  425.             conn, cmd=cmd, strip_cmd=strip_cmd,
  426.             delim=delim, strip_delim=strip_delim)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement