Refactor AdbTools into its own module.

Also fix up device connection status issue.

Bug: 122826931
Test: atest acloud_test &&
acloud create
adb kill-server
acloud list should display "not connected" status

Change-Id: I27b22f5f2368790c22136140432a1ae5b2947e07
diff --git a/list/instance.py b/list/instance.py
index 51d7817..d726839 100644
--- a/list/instance.py
+++ b/list/instance.py
@@ -38,6 +38,7 @@
 
 from acloud.internal import constants
 from acloud.internal.lib import utils
+from acloud.internal.lib.adb_tools import AdbTools
 
 logger = logging.getLogger(__name__)
 
@@ -259,8 +260,15 @@
     def _ProcessGceInstance(self, gce_instance):
         """Parse the required data from gce_instance to local variables.
 
-        We also gather more details on client side including the forwarding adb port
-        and vnc port which will be used to determine the status of connection.
+        We also gather more details on client side including the forwarding adb
+        port and vnc port which will be used to determine the status of ssh
+        tunnel connection.
+
+        The status of gce instance will be displayed in _fullname property:
+        - Connected: If gce instance and ssh tunnel and adb connection are all
+         active.
+        - No connected: If ssh tunnel or adb connection is not found.
+        - Terminated: If we can't retrieve the public ip from gce instance.
 
         Args:
            gce_instance: dict object queried from gce.
@@ -294,14 +302,15 @@
             self._ip = ip
             self._adb_port = forwarded_ports.adb_port
             self._vnc_port = forwarded_ports.vnc_port
-            if self._adb_port:
-                self._ssh_tunnel_is_connected = True
+            self._ssh_tunnel_is_connected = self._adb_port is not None
+
+            adb_device = AdbTools(self._adb_port)
+            if adb_device.IsAdbConnected():
                 self._fullname = (_FULL_NAME_STRING %
                                   {"device_serial": "127.0.0.1:%d" % self._adb_port,
                                    "instance_name": self._name,
                                    "elapsed_time": self._elapsed_time})
             else:
-                self._ssh_tunnel_is_connected = False
                 self._fullname = (_FULL_NAME_STRING %
                                   {"device_serial": "not connected",
                                    "instance_name": self._name,
diff --git a/list/instance_test.py b/list/instance_test.py
index 3178adc..e0f4ffd 100644
--- a/list/instance_test.py
+++ b/list/instance_test.py
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Tests for instance class."""
+
 import collections
 import datetime
 import subprocess
@@ -27,6 +28,7 @@
 
 from acloud.internal import constants
 from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib.adb_tools import AdbTools
 from acloud.list import instance
 
 
@@ -120,6 +122,7 @@
             "GetAdbVncPortFromSSHTunnel",
             return_value=forwarded_ports(vnc_port=fake_vnc, adb_port=fake_adb))
         self.Patch(instance, "_GetElapsedTime", return_value="fake_time")
+        self.Patch(AdbTools, "IsAdbConnected", return_value=True)
 
         # test ssh_tunnel_is_connected will be true if ssh tunnel connection is found
         instance_info = instance.RemoteInstance(gce_instance)
@@ -130,6 +133,14 @@
             fake_adb, gce_instance[constants.INS_KEY_NAME], "fake_time")
         self.assertEqual(expected_full_name, instance_info.fullname)
 
+        # test ssh tunnel is connected but adb is disconnected
+        self.Patch(AdbTools, "IsAdbConnected", return_value=False)
+        instance_info = instance.RemoteInstance(gce_instance)
+        self.assertTrue(instance_info.ssh_tunnel_is_connected)
+        expected_full_name = "device serial: not connected (%s) elapsed time: %s" % (
+            instance_info.name, "fake_time")
+        self.assertEqual(expected_full_name, instance_info.fullname)
+
         # test ssh_tunnel_is_connected will be false if ssh tunnel connection is not found
         self.Patch(
             instance.RemoteInstance,