android: Remove skylab-isms from test runner; support network devices

With some recent work in crbug.com/404887520, desktop DUTs hosted in
the CrOS lab now have a more consistent API. Namely: all DUTs have the
same hostname (but they'll resolve to different IPs on each bot).
So we can specify that single hostname in the testing specs for the
bots, and let the network magic resolve it as needed.

This lets us clean up a bunch of the skylab/swarming bits in the
android test runner.

This also makes the "connect over network" support a little more
generic, so someone should be able to use this test runner to run
tests on a device over the network.

The desktop tester will need both this and
https://crrev.com/i/8127885.

Bug: 404887520, 368004900
Change-Id: Id260855e760f07957beb2c7180ef271f791835eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6382279
Reviewed-by: Haiyang Pan <[email protected]>
Reviewed-by: Andrew Grieve <[email protected]>
Commit-Queue: Ben Pastene <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1437718}
diff --git a/build/android/pylib/base/environment_factory.py b/build/android/pylib/base/environment_factory.py
index 05e41b1d..7c7d9119 100644
--- a/build/android/pylib/base/environment_factory.py
+++ b/build/android/pylib/base/environment_factory.py
@@ -2,12 +2,9 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
-
 from pylib import constants
 from pylib.local.device import local_device_environment
-from pylib.local.device import skylab_environment
-
+from pylib.local.device import local_device_network_environment
 from pylib.local.machine import local_machine_environment
 
 try:
@@ -30,16 +27,9 @@
         return local_emulator_environment.LocalEmulatorEnvironment(
             args, output_manager, error_func)
 
-      if args.connect_over_ethernet:
-        swarming_server = os.environ.get('SWARMING_SERVER', '')
-        if skylab_environment.SWARMING_SERVER not in swarming_server:
-          error_func(
-              'connect-over-ethernet is only supported for tasks running on '
-              f'{skylab_environment.SWARMING_SERVER}. '
-              f'SWARMING_SERVER={swarming_server}')
-          raise RuntimeError('error_func must call exit inside.')
-        return skylab_environment.SkylabEnvironment(args, output_manager,
-                                                    error_func)
+      if args.connect_over_network:
+        return local_device_network_environment.LocalDeviceNetworkEnvironment(
+            args, output_manager, error_func)
 
       return local_device_environment.LocalDeviceEnvironment(
           args, output_manager, error_func)
diff --git a/build/android/pylib/local/device/local_device_ethernet_environment.py b/build/android/pylib/local/device/local_device_ethernet_environment.py
deleted file mode 100644
index 6977f05..0000000
--- a/build/android/pylib/local/device/local_device_ethernet_environment.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import logging
-
-from devil.android.sdk import adb_wrapper
-
-from pylib.local.device import local_device_environment
-
-
-class LocalDeviceEthernetEnvironment(
-    local_device_environment.LocalDeviceEnvironment):
-  """
-  A subclass of LocalDeviceEnvironment for devices connected over ethernet.
-
-  This class cannot be instantiated. Subclasses should implement the
-  GetDeviceHostname method, as this is specific to each environment.
-  """
-
-  def __init__(self, args, output_manager, error_func):
-    super().__init__(args, output_manager, error_func)
-    hostname = self.GetDeviceHostname()
-    logging.info('connecting to %s', hostname)
-    adb_wrapper.AdbWrapper.Connect(hostname)
-
-  def GetDeviceHostname(self):
-    raise NotImplementedError
diff --git a/build/android/pylib/local/device/local_device_network_environment.py b/build/android/pylib/local/device/local_device_network_environment.py
new file mode 100644
index 0000000..a0163eb
--- /dev/null
+++ b/build/android/pylib/local/device/local_device_network_environment.py
@@ -0,0 +1,19 @@
+# Copyright 2025 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+from devil.android.sdk import adb_wrapper
+
+from pylib.local.device import local_device_environment
+
+
+class LocalDeviceNetworkEnvironment(
+    local_device_environment.LocalDeviceEnvironment):
+  """A subclass of LocalDeviceEnvironment for devices connected over TCP/IP."""
+
+  def __init__(self, args, output_manager, error_func):
+    super().__init__(args, output_manager, error_func)
+    logging.info('connecting to %s', args.test_devices[0])
+    adb_wrapper.AdbWrapper.Connect(args.test_devices[0])
diff --git a/build/android/pylib/local/device/skylab_environment.py b/build/android/pylib/local/device/skylab_environment.py
deleted file mode 100644
index 22b892e..0000000
--- a/build/android/pylib/local/device/skylab_environment.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-
-from pylib.local.device import local_device_ethernet_environment
-
-SWARMING_SERVER = 'chromeos-swarming.appspot.com'
-
-
-class SkylabEnvironment(
-    local_device_ethernet_environment.LocalDeviceEthernetEnvironment):
-  """
-  A subclass of LocalDeviceEthernetEnvironment for Skylab devices.
-  """
-
-  def GetDeviceHostname(self):
-    """Return the hostname based on the bot id.
-
-    Strips the first component of the bot id, e.g. 'cros-clank1' -> 'clank1'.
-
-    Gets the bot id from the SWARMING_BOT_ID envvar, see
-    https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/appengine/swarming/doc/Magic-Values.md#bot-environment-variables.
-    """
-    bot_id = os.environ.get('SWARMING_BOT_ID')
-    if not bot_id:
-      raise ValueError(
-          "device_arg is 'swarming' but SWARMING_BOT_ID is not set")
-
-    return bot_id[bot_id.index("-") + 1:]
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 2422c04..49232d0 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -361,10 +361,10 @@
       'secondary user, e.g. Android Automotive OS.')
 
   parser.add_argument(
-      '--connect-over-ethernet',
+      '--connect-over-network',
       action='store_true',
-      help='Connect to devices over the network using "adb connect". Only '
-      'supported when running on chromeos-swarming')
+      help='Connect to devices over the network using "adb connect". Must '
+      'specify device hostnames/IPs via "-d"/"--device" args.')
 
 
 def AddEmulatorOptions(parser):
@@ -1524,6 +1524,11 @@
       or getattr(args, 'wait_for_java_debugger', None)):
     args.num_retries = 0
 
+  if (getattr(args, 'connect_over_network', False)
+      and len(getattr(args, 'test_devices', [])) != 1):
+    parser.error('Need to specify a single device (via "--device") when using '
+                 '--connect-over-network.')
+
   # Result-sink may not exist in the environment if rdb stream is not enabled.
   result_sink_client = result_sink.TryInitClient()
 
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 9fa8528..c041778b 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -181,12 +181,11 @@
 pylib/local/__init__.py
 pylib/local/device/__init__.py
 pylib/local/device/local_device_environment.py
-pylib/local/device/local_device_ethernet_environment.py
 pylib/local/device/local_device_gtest_run.py
 pylib/local/device/local_device_instrumentation_test_run.py
 pylib/local/device/local_device_monkey_test_run.py
+pylib/local/device/local_device_network_environment.py
 pylib/local/device/local_device_test_run.py
-pylib/local/device/skylab_environment.py
 pylib/local/emulator/__init__.py
 pylib/local/emulator/avd.py
 pylib/local/emulator/ini.py