This repository was archived by the owner on Jan 10, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 363
AdbCommands structure change, Pycryptodome support, python3 fixes #82
Merged
Merged
Changes from all commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
70fd2dd
Added pycharm .idea/ folder to gitignore
greateggsgreg 6e4e466
Added authsigner for pycryptodome
greateggsgreg 54b3ed8
Added support for retrieving the reason for a command failure
greateggsgreg fd30265
Revised AdbCommands model to be an instantiated class. Fixed misc bug…
greateggsgreg 3f7eefb
Merge adb_test and CONTRIBUTORS from master
greateggsgreg 0eca1c7
Additional formatting cleanup for common_cli
greateggsgreg a3b6c22
Fixed formatting in filesync_protocol
greateggsgreg 7b1c115
common.py merges with master and spacing fixes
greateggsgreg 7b39bc3
Fixed spacing issues in adb_protocol. Removed unused code
greateggsgreg 240279e
Added future to the requirements for py3 compatibility
greateggsgreg 14a0f2f
Fixed spacing issues in adb_debug
greateggsgreg 13d75f8
Fixed up adb_commands spacing
greateggsgreg 60ecb1c
Minor spacing change to requirements list
greateggsgreg 71aa542
Updated InteractiveShellCommand to handle changing delimiters, still …
greateggsgreg dc7bafd
Dynamic detection of rsa library in setup.py
greateggsgreg c4009e4
Improved user experience for adb_debug
greateggsgreg 04bf05a
Shortened InteractiveShell function and parameters to less than 80 ch…
greateggsgreg 8f1120a
Merge branch 'master' into python3-and-misc-fixes
greateggsgreg a4113e5
adb_commands: Exposing handle paramter to allow caller to specify the…
greateggsgreg 32f2532
Migrated fastboot to use object oriented interface. Updated fastboot …
greateggsgreg e014cdc
AdbCommands: Improved handle interface and passing to internal functions
greateggsgreg 71825c5
Updated adb_test.py to conform to new AdbCommand object model
greateggsgreg 8420ba3
Updated adb_test to support py2/3. Fixed exit bug in adb_commands
greateggsgreg 45937ce
Fixed merge conflicts with master
greateggsgreg 855b33d
added .DS_Store files to gitignore
greateggsgreg 821d962
Fixed minor syntax issue in function declaration
greateggsgreg 550b39e
Fixed merge issues
greateggsgreg fd09a4f
removed reliance on future library
greateggsgreg 06babd6
Improved FastbootCommands.ConnectDevice documentation. Fixed bug in f…
greateggsgreg e555496
FilesyncProtocol: Simplified handling of utf-8 decoding errors
greateggsgreg b6284bf
Imported __future__.print_function for python2/3 compatible printing …
greateggsgreg 257b3ad
AdbProtocol: Reverted reliance on future.iteritems
greateggsgreg 636ceba
Made handle a protected member, removed unused code, fixed line spaci…
greateggsgreg a308704
Simplified logic in fastboot.ConnectDevice
greateggsgreg d063e33
Fixed merge conflicts
greateggsgreg 6d7404e
setup.py: Removed duplicate version string for M2Crypto requirements
greateggsgreg 7a1a1c7
Fixed check for bytes in filesync_protocol
greateggsgreg 45f0f0a
Updated documentation around self._service_connections
greateggsgreg c8fcbc4
Additional docstrings for progress_callback parameter
greateggsgreg 2eea6b9
Updated adb_commands._Connect to be a protected member that could be …
greateggsgreg 5badca3
Updated contributors
greateggsgreg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,5 @@ adb.egg-info/ | |
.tox/ | ||
/adb.zip | ||
/fastboot.zip | ||
.idea/ | ||
*.DS_Store* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
import io | ||
import os | ||
import socket | ||
import posixpath | ||
|
||
from adb import adb_protocol | ||
from adb import common | ||
|
@@ -54,52 +55,127 @@ class AdbCommands(object): | |
protocol_handler = adb_protocol.AdbMessage | ||
filesync_handler = filesync_protocol.FilesyncProtocol | ||
|
||
@classmethod | ||
def ConnectDevice( | ||
cls, port_path=None, serial=None, default_timeout_ms=None, **kwargs): | ||
"""Convenience function to get an adb device from usb path or serial. | ||
def __init__(self): | ||
|
||
self.__reset() | ||
|
||
def __reset(self): | ||
self.build_props = None | ||
self._handle = None | ||
self._device_state = None | ||
|
||
# Connection table tracks each open AdbConnection objects per service type for program functions | ||
# that choose to persist an AdbConnection object for their functionality, using | ||
# self._get_service_connection | ||
self._service_connections = {} | ||
|
||
def _get_service_connection(self, service, service_command=None, create=True, timeout_ms=None): | ||
""" | ||
Based on the service, get the AdbConnection for that service or create one if it doesnt exist | ||
|
||
:param service: | ||
:param service_command: Additional service parameters to append | ||
:param create: If False, dont create a connection if it does not exist | ||
:return: | ||
""" | ||
|
||
connection = self._service_connections.get(service, None) | ||
|
||
if connection: | ||
return connection | ||
|
||
if not connection and not create: | ||
return None | ||
|
||
if service_command: | ||
destination_str = b'%s:%s' % (service, service_command) | ||
else: | ||
destination_str = service | ||
|
||
connection = self.protocol_handler.Open( | ||
self._handle, destination=destination_str, timeout_ms=timeout_ms) | ||
|
||
self._service_connections.update({service: connection}) | ||
|
||
return connection | ||
|
||
def ConnectDevice(self, port_path=None, serial=None, default_timeout_ms=None, **kwargs): | ||
"""Convenience function to setup a transport handle for the adb device from | ||
usb path or serial then connect to it. | ||
|
||
Args: | ||
port_path: The filename of usb port to use. | ||
serial: The serial number of the device to use. | ||
default_timeout_ms: The default timeout in milliseconds to use. | ||
kwargs: handle: Device handle to use (instance of common.TcpHandle or common.UsbHandle) | ||
banner: Connection banner to pass to the remote device | ||
rsa_keys: List of AuthSigner subclass instances to be used for | ||
authentication. The device can either accept one of these via the Sign | ||
method, or we will send the result of GetPublicKey from the first one | ||
if the device doesn't accept any of them. | ||
auth_timeout_ms: Timeout to wait for when sending a new public key. This | ||
is only relevant when we send a new public key. The device shows a | ||
dialog and this timeout is how long to wait for that dialog. If used | ||
in automation, this should be low to catch such a case as a failure | ||
quickly; while in interactive settings it should be high to allow | ||
users to accept the dialog. We default to automation here, so it's low | ||
by default. | ||
|
||
If serial specifies a TCP address:port, then a TCP connection is | ||
used instead of a USB connection. | ||
""" | ||
if serial and b':' in serial: | ||
handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms) | ||
|
||
# If there isnt a handle override (used by tests), build one here | ||
if 'handle' in kwargs: | ||
self._handle = kwargs.pop('handle') | ||
elif serial and b':' in serial: | ||
self._handle = common.TcpHandle(serial, timeout_ms=default_timeout_ms) | ||
else: | ||
handle = common.UsbHandle.FindAndOpen( | ||
self._handle = common.UsbHandle.FindAndOpen( | ||
DeviceIsAvailable, port_path=port_path, serial=serial, | ||
timeout_ms=default_timeout_ms) | ||
return cls.Connect(handle, **kwargs) | ||
|
||
def __init__(self, handle, device_state): | ||
self.handle = handle | ||
self._device_state = device_state | ||
self._Connect(**kwargs) | ||
|
||
return self | ||
|
||
def Close(self): | ||
self.handle.Close() | ||
for conn in list(self._service_connections.values()): | ||
if conn: | ||
try: | ||
conn.Close() | ||
except: | ||
pass | ||
|
||
@classmethod | ||
def Connect(cls, usb, banner=None, **kwargs): | ||
if self._handle: | ||
self._handle.Close() | ||
|
||
self.__reset() | ||
|
||
def _Connect(self, banner=None, **kwargs): | ||
"""Connect to the device. | ||
|
||
Args: | ||
usb: UsbHandle or TcpHandle instance to use. | ||
banner: See protocol_handler.Connect. | ||
**kwargs: See protocol_handler.Connect for kwargs. Includes rsa_keys, | ||
and auth_timeout_ms. | ||
**kwargs: See protocol_handler.Connect and adb_commands.ConnectDevice for kwargs. | ||
Includes handle, rsa_keys, and auth_timeout_ms. | ||
Returns: | ||
An instance of this class if the device connected successfully. | ||
""" | ||
|
||
if not banner: | ||
banner = socket.gethostname().encode() | ||
device_state = cls.protocol_handler.Connect(usb, banner=banner, **kwargs) | ||
|
||
conn_str = self.protocol_handler.Connect(self._handle, banner=banner, **kwargs) | ||
|
||
# Remove banner and colons after device state (state::banner) | ||
device_state = device_state.split(b':')[0] | ||
return cls(usb, device_state) | ||
parts = conn_str.split(b'::') | ||
self._device_state = parts[0] | ||
|
||
# Break out the build prop info | ||
self.build_props = str(parts[1].split(b';')) | ||
|
||
return True | ||
|
||
@classmethod | ||
def Devices(cls): | ||
|
@@ -129,13 +205,13 @@ def Install(self, apk_path, destination_dir='', timeout_ms=None, replace_existin | |
if not destination_dir: | ||
destination_dir = '/data/local/tmp/' | ||
basename = os.path.basename(apk_path) | ||
destination_path = destination_dir + basename | ||
destination_path = posixpath.join(destination_dir, basename) | ||
self.Push(apk_path, destination_path, timeout_ms=timeout_ms, progress_callback=transfer_progress_callback) | ||
|
||
cmd = ['pm install'] | ||
if replace_existing: | ||
cmd.append('-r') | ||
cmd.append('"%s"' % destination_path) | ||
cmd.append('"{}"'.format(destination_path)) | ||
return self.Shell(' '.join(cmd), timeout_ms=timeout_ms) | ||
|
||
def Uninstall(self, package_name, keep_data=False, timeout_ms=None): | ||
|
@@ -167,50 +243,56 @@ def Push(self, source_file, device_filename, mtime='0', timeout_ms=None, progres | |
progress_callback: callback method that accepts filename, bytes_written and total_bytes, | ||
total_bytes will be -1 for file-like objects | ||
""" | ||
should_close = False | ||
if isinstance(source_file, str): | ||
if os.path.isdir(source_file): | ||
self.Shell("mkdir " + device_filename) | ||
for f in os.listdir(source_file): | ||
self.Push(os.path.join(source_file, f), device_filename + '/' + f, progress_callback=progress_callback) | ||
return | ||
source_file = open(source_file, "rb") | ||
should_close = True | ||
|
||
connection = self.protocol_handler.Open( | ||
self.handle, destination=b'sync:', timeout_ms=timeout_ms) | ||
self.filesync_handler.Push(connection, source_file, device_filename, | ||
with source_file: | ||
connection = self.protocol_handler.Open( | ||
self._handle, destination=b'sync:', timeout_ms=timeout_ms) | ||
self.filesync_handler.Push(connection, source_file, device_filename, | ||
mtime=int(mtime), progress_callback=progress_callback) | ||
if should_close: | ||
source_file.close() | ||
connection.Close() | ||
|
||
def Pull(self, device_filename, dest_file='', progress_callback=None, timeout_ms=None): | ||
def Pull(self, device_filename, dest_file=None, timeout_ms=None, progress_callback=None): | ||
"""Pull a file from the device. | ||
|
||
Args: | ||
device_filename: Filename on the device to pull. | ||
dest_file: If set, a filename or writable file-like object. | ||
timeout_ms: Expected timeout for any part of the pull. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you document There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
progress_callback: callback method that accepts filename, bytes_written and total_bytes, | ||
total_bytes will be -1 for file-like objects | ||
|
||
Returns: | ||
The file data if dest_file is not set. | ||
The file data if dest_file is not set. Otherwise, True if the destination file exists | ||
""" | ||
if not dest_file: | ||
dest_file = io.BytesIO() | ||
elif isinstance(dest_file, str): | ||
dest_file = open(dest_file, 'wb') | ||
connection = self.protocol_handler.Open( | ||
self.handle, destination=b'sync:', | ||
timeout_ms=timeout_ms) | ||
self.filesync_handler.Pull(connection, device_filename, dest_file, progress_callback) | ||
connection.Close() | ||
dest_file = open(dest_file, 'w') | ||
else: | ||
raise ValueError("destfile is of unknown type") | ||
|
||
conn = self.protocol_handler.Open( | ||
self._handle, destination=b'sync:', timeout_ms=timeout_ms) | ||
|
||
self.filesync_handler.Pull(conn, device_filename, dest_file, progress_callback) | ||
|
||
conn.Close() | ||
if isinstance(dest_file, io.BytesIO): | ||
return dest_file.getvalue() | ||
else: | ||
dest_file.close() | ||
return os.path.exists(dest_file) | ||
|
||
def Stat(self, device_filename): | ||
"""Get a file's stat() information.""" | ||
connection = self.protocol_handler.Open(self.handle, destination=b'sync:') | ||
connection = self.protocol_handler.Open(self._handle, destination=b'sync:') | ||
mode, size, mtime = self.filesync_handler.Stat( | ||
connection, device_filename) | ||
connection.Close() | ||
|
@@ -222,7 +304,7 @@ def List(self, device_path): | |
Args: | ||
device_path: Directory to list. | ||
""" | ||
connection = self.protocol_handler.Open(self.handle, destination=b'sync:') | ||
connection = self.protocol_handler.Open(self._handle, destination=b'sync:') | ||
listing = self.filesync_handler.List(connection, device_path) | ||
connection.Close() | ||
return listing | ||
|
@@ -233,32 +315,37 @@ def Reboot(self, destination=b''): | |
Args: | ||
destination: Specify 'bootloader' for fastboot. | ||
""" | ||
self.protocol_handler.Open(self.handle, b'reboot:%s' % destination) | ||
self.protocol_handler.Open(self._handle, b'reboot:%s' % destination) | ||
|
||
def RebootBootloader(self): | ||
"""Reboot device into fastboot.""" | ||
self.Reboot(b'bootloader') | ||
|
||
def Remount(self): | ||
"""Remount / as read-write.""" | ||
return self.protocol_handler.Command(self.handle, service=b'remount') | ||
return self.protocol_handler.Command(self._handle, service=b'remount') | ||
|
||
def Root(self): | ||
"""Restart adbd as root on the device.""" | ||
return self.protocol_handler.Command(self.handle, service=b'root') | ||
return self.protocol_handler.Command(self._handle, service=b'root') | ||
|
||
def EnableVerity(self): | ||
"""Re-enable dm-verity checking on userdebug builds""" | ||
return self.protocol_handler.Command(self.handle, service=b'enable-verity') | ||
return self.protocol_handler.Command(self._handle, service=b'enable-verity') | ||
|
||
def DisableVerity(self): | ||
"""Disable dm-verity checking on userdebug builds""" | ||
return self.protocol_handler.Command(self.handle, service=b'disable-verity') | ||
return self.protocol_handler.Command(self._handle, service=b'disable-verity') | ||
|
||
def Shell(self, command, timeout_ms=None): | ||
"""Run command on the device, returning the output.""" | ||
"""Run command on the device, returning the output. | ||
|
||
Args: | ||
command: Shell command to run | ||
timeout_ms: Maximum time to allow the command to run. | ||
""" | ||
return self.protocol_handler.Command( | ||
self.handle, service=b'shell', command=command, | ||
self._handle, service=b'shell', command=command, | ||
timeout_ms=timeout_ms) | ||
|
||
def StreamingShell(self, command, timeout_ms=None): | ||
|
@@ -272,13 +359,34 @@ def StreamingShell(self, command, timeout_ms=None): | |
The responses from the shell command. | ||
""" | ||
return self.protocol_handler.StreamingCommand( | ||
self.handle, service=b'shell', command=command, | ||
self._handle, service=b'shell', command=command, | ||
timeout_ms=timeout_ms) | ||
|
||
def Logcat(self, options, timeout_ms=None): | ||
"""Run 'shell logcat' and stream the output to stdout. | ||
|
||
Args: | ||
options: Arguments to pass to 'logcat'. | ||
timeout_ms: Maximum time to allow the command to run. | ||
""" | ||
return self.StreamingShell('logcat %s' % options, timeout_ms) | ||
|
||
def InteractiveShell(self, cmd=None, strip_cmd=True, delim=None, strip_delim=True): | ||
"""Get stdout from the currently open interactive shell and optionally run a command | ||
on the device, returning all output. | ||
|
||
Args: | ||
command: Optional. Command to run on the target. | ||
strip_cmd: Optional (default True). Strip command name from stdout. | ||
delim: Optional. Delimiter to look for in the output to know when to stop expecting more output | ||
(usually the shell prompt) | ||
strip_delim: Optional (default True): Strip the provided delimiter from the output | ||
|
||
Returns: | ||
The stdout from the shell command. | ||
""" | ||
conn = self._get_service_connection(b'shell:') | ||
|
||
return self.protocol_handler.InteractiveShellCommand( | ||
conn, cmd=cmd, strip_cmd=strip_cmd, | ||
delim=delim, strip_delim=strip_delim) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why reorder the args?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previous versions of this library had timeout_ms as the 3rd argument. The recent addition of progress_callback broke the order in which the arguments were expected, so I push the new argument back