From baa7ad16504a20c4af8d80bba9b172163640ca91 Mon Sep 17 00:00:00 2001
From: Marc Bonnici <marc.bonnici@arm.com>
Date: Thu, 7 Nov 2019 09:59:48 +0000
Subject: [PATCH] devlib/AndroidTarget: Move adb specific commands into the ADB
 connection

The `AndroidTarget` class should not depend on ADB specific commands as
is is possible to use this target with other connection types e.g. ssh.
Therefore move the adb specific commands into the `AdbConnection`.

- `wait_for_device` and `reboot_bootloader` are now exposed in AndroidTarget
as generic methods and call through to the connection method.
- `adb_kill_server` is now a standalone function of the AdbConnection.
---
 devlib/host.py          |  6 ++++++
 devlib/target.py        | 24 ++++--------------------
 devlib/utils/android.py | 10 +++++++++-
 devlib/utils/ssh.py     | 19 +++++++++++++++++++
 doc/target.rst          |  8 ++++++++
 5 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/devlib/host.py b/devlib/host.py
index 052139b..e040b16 100644
--- a/devlib/host.py
+++ b/devlib/host.py
@@ -109,6 +109,12 @@ class LocalConnection(object):
     def cancel_running_command(self):
         pass
 
+    def wait_for_device(self, timeout=30):
+        return
+
+    def reboot_bootloader(self, timeout=30):
+        raise NotImplementedError()
+
     def _get_password(self):
         if self.password:
             return self.password
diff --git a/devlib/target.py b/devlib/target.py
index b583839..46a89d4 100644
--- a/devlib/target.py
+++ b/devlib/target.py
@@ -1530,27 +1530,11 @@ class AndroidTarget(Target):
     def get_logcat_monitor(self, regexps=None):
         return LogcatMonitor(self, regexps)
 
-    def adb_kill_server(self, timeout=30):
-        if not isinstance(self.conn, AdbConnection):
-            raise TargetStableError('Cannot issues adb command without adb connection')
-        adb_command(self.adb_name, 'kill-server', timeout)
+    def wait_for_device(self, timeout=30):
+        self.conn.wait_for_device()
 
-    def adb_wait_for_device(self, timeout=30):
-        if not isinstance(self.conn, AdbConnection):
-            raise TargetStableError('Cannot issues adb command without adb connection')
-        adb_command(self.adb_name, 'wait-for-device', timeout)
-
-    def adb_reboot_bootloader(self, timeout=30):
-        if not isinstance(self.conn, AdbConnection):
-            raise TargetStableError('Cannot issues adb command without adb connection')
-        adb_command(self.adb_name, 'reboot-bootloader', timeout)
-
-    def adb_root(self, enable=True, force=False):
-        if not isinstance(self.conn, AdbConnection):
-            raise TargetStableError('Cannot enable adb root without adb connection')
-        if enable and self.connected_as_root and not force:
-            return
-        self.conn.adb_root(enable=enable)
+    def reboot_bootloader(self, timeout=30):
+        self.conn.reboot_bootloader()
 
     def is_screen_on(self):
         output = self.execute('dumpsys power')
diff --git a/devlib/utils/android.py b/devlib/utils/android.py
index ca36643..70a8df0 100755
--- a/devlib/utils/android.py
+++ b/devlib/utils/android.py
@@ -318,7 +318,7 @@ class AdbConnection(object):
         AdbConnection.active_connections[self.device] -= 1
         if AdbConnection.active_connections[self.device] <= 0:
             if self.adb_as_root:
-                adb_root(self.device, enable=False)
+                self.adb_root(self.device, enable=False)
             adb_disconnect(self.device)
             del AdbConnection.active_connections[self.device]
 
@@ -335,6 +335,12 @@ class AdbConnection(object):
             raise TargetStableError(output)
         AdbConnection._connected_as_root[self.device] = enable
 
+    def wait_for_device(self, timeout=30):
+        adb_command(self.device, 'wait-for-device', timeout)
+
+    def reboot_bootloader(self, timeout=30):
+        adb_command(self.device, 'reboot-bootloader', timeout)
+
     # Again, we need to handle boards where the default output format from ls is
     # single column *and* boards where the default output is multi-column.
     # We need to do this purely because the '-1' option causes errors on older
@@ -544,6 +550,8 @@ def adb_background_shell(device, command,
     logger.debug(full_command)
     return subprocess.Popen(full_command, stdout=stdout, stderr=stderr, shell=True)
 
+def adb_kill_server(self, timeout=30):
+    adb_command(None, 'kill-server', timeout)
 
 def adb_list_devices(adb_server=None):
     output = adb_command(None, 'devices', adb_server=adb_server)
diff --git a/devlib/utils/ssh.py b/devlib/utils/ssh.py
index aabb19d..056ee32 100644
--- a/devlib/utils/ssh.py
+++ b/devlib/utils/ssh.py
@@ -299,6 +299,12 @@ class SshConnection(object):
                 return True
         return False
 
+    def wait_for_device(self, timeout=30):
+        return
+
+    def reboot_bootloader(self, timeout=30):
+        raise NotImplementedError()
+
     def _execute_and_wait_for_prompt(self, command, timeout=None, as_root=False, strip_colors=True, log=True):
         self.conn.prompt(0.1)  # clear an existing prompt if there is one.
         if as_root and self.connected_as_root:
@@ -634,6 +640,19 @@ class Gem5Connection(TelnetConnection):
         # Delete the lock file
         os.remove(self.lock_file_name)
 
+    def wait_for_device(self, timeout=30):
+        """
+        Wait for Gem5 to be ready for interation with a timeout.
+        """
+        for _ in attempts(timeout):
+            if self.ready:
+                return
+            time.sleep(1)
+        raise TimeoutError('Gem5 is not ready for interaction')
+
+    def reboot_bootloader(self, timeout=30):
+        raise NotImplementedError()
+
     # Functions only to be called by the Gem5 connection itself
     def _connect_gem5_platform(self, platform):
         port = platform.gem5_port
diff --git a/doc/target.rst b/doc/target.rst
index 1d4ff76..c6dfaa8 100644
--- a/doc/target.rst
+++ b/doc/target.rst
@@ -647,6 +647,14 @@ Android Target
    Returns ``True`` if the targets screen is currently on and ``False``
    otherwise.
 
+.. method:: AndroidTarget.wait_for_target(timeout=30)
+
+    Returns when the devices becomes available withing the given timeout
+    otherwise returns a ``TimeoutError``.
+
+.. method:: AndroidTarget.reboot_bootloader(timeout=30)
+    Attempts to reboot the target into it's bootloader.
+
 .. method:: AndroidTarget.homescreen()
 
    Returns the device to its home screen.