From 5a32f3a51508f1900528523edda02c08135748b1 Mon Sep 17 00:00:00 2001 From: slicen Date: Thu, 14 Jan 2021 15:21:50 +1100 Subject: [PATCH 1/2] Fix incorrect timestamp type in asynchronous push The incorrect timestamp type caused all asynchronous pushes to fail. --- ppadb/sync_async/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppadb/sync_async/__init__.py b/ppadb/sync_async/__init__.py index 4f65540..012c354 100644 --- a/ppadb/sync_async/__init__.py +++ b/ppadb/sync_async/__init__.py @@ -20,7 +20,7 @@ def _get_src_info(src): if not exists: return exists, None, None - timestamp = os.stat(src).st_mtime + timestamp = int(os.stat(src).st_mtime) total_size = os.path.getsize(src) return exists, timestamp, total_size From 29e9cdf73da7c5693c020cd7fe766235ffb58713 Mon Sep 17 00:00:00 2001 From: slicen Date: Thu, 14 Jan 2021 15:24:11 +1100 Subject: [PATCH 2/2] Add asynchronous install and uninstall commands Based on the synchronous equivalents. --- ppadb/device_async.py | 70 +++++++++++++++++++++++++++++++++++- ppadb/sync_async/__init__.py | 5 +++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/ppadb/device_async.py b/ppadb/device_async.py index 5e4a8b0..353cb82 100644 --- a/ppadb/device_async.py +++ b/ppadb/device_async.py @@ -3,11 +3,22 @@ except ImportError: # pragma: no cover from asyncio import get_event_loop as get_running_loop # Python 3.6 compatibility -import re import os +import re +from ppadb import InstallError from ppadb.command.transport_async import TransportAsync from ppadb.sync_async import SyncAsync +from ppadb.utils.logger import AdbLogging + +try: + from shlex import quote as cmd_quote +except ImportError: + from pipes import quote as cmd_quote + + +logger = AdbLogging.get_logger(__name__) + def _get_src_info(src): @@ -67,3 +78,60 @@ async def pull(self, src, dest): async with sync_conn: return await sync.pull(src, dest) + + async def install(self, path, + forward_lock=False, # -l + reinstall=False, # -r + test=False, # -t + installer_package_name="", # -i {installer_package_name} + shared_mass_storage=False, # -s + internal_system_memory=False, # -f + downgrade=False, # -d + grand_all_permissions=False # -g + ): + dest = SyncAsync.temp(path) + + try: + await self.push(path, dest) + except Exception: + raise InstallError("file transfer to device failed") + + parameters = [] + if forward_lock: parameters.append("-l") + if reinstall: parameters.append("-r") + if test: parameters.append("-t") + if len(installer_package_name) > 0: parameters.append("-i {}".format(installer_package_name)) + if shared_mass_storage: parameters.append("-s") + if internal_system_memory: parameters.append("-f") + if downgrade: parameters.append("-d") + if grand_all_permissions: parameters.append("-g") + + try: + result = await self.shell( + "pm install {} {}".format(" ".join(parameters), cmd_quote(dest)) + ) + match = re.search(self.INSTALL_RESULT_PATTERN, result) + + if match and match.group(1) == "Success": + return True + elif match: + groups = match.groups() + raise InstallError(dest, groups[1]) + else: + raise InstallError(dest, result) + finally: + await self.shell("rm -f {}".format(dest)) + + async def uninstall(self, package): + result = await self.shell("pm uninstall {}".format(package)) + + m = re.search(self.UNINSTALL_RESULT_PATTERN, result) + + if m and m.group(1) == "Success": + return True + elif m: + logger.error(m.group(1)) + return False + else: + logger.error("There is no message after uninstalling") + return False diff --git a/ppadb/sync_async/__init__.py b/ppadb/sync_async/__init__.py index 012c354..fdfbb45 100644 --- a/ppadb/sync_async/__init__.py +++ b/ppadb/sync_async/__init__.py @@ -27,11 +27,16 @@ def _get_src_info(src): class SyncAsync: + TEMP_PATH = '/data/local/tmp' DATA_MAX_LENGTH = 65536 def __init__(self, connection): self.connection = connection + @staticmethod + def temp(path): + return "{}/{}".format(SyncAsync.TEMP_PATH, os.path.basename(path)) + async def push(self, src, dest, mode, progress=None): """Push from local path |src| to |dest| on device. :param progress: callback, called with (filename, total_size, sent_size)