Replace netls and netaddr with dev_finder (#26090)

diff --git a/packages/flutter_tools/bin/fuchsia_attach.dart b/packages/flutter_tools/bin/fuchsia_attach.dart
index 71c0d9f..0180b73 100644
--- a/packages/flutter_tools/bin/fuchsia_attach.dart
+++ b/packages/flutter_tools/bin/fuchsia_attach.dart
@@ -41,6 +41,7 @@
   final String buildDirectory = argResults['build-dir'];
   final File frontendServer = fs.file('$buildDirectory/host_x64/gen/third_party/flutter/frontend_server/frontend_server_tool.snapshot');
   final File sshConfig = fs.file('$buildDirectory/ssh-keys/ssh_config');
+  final File devFinder = fs.file('$buildDirectory/host_x64/dev_finder');
   final File platformKernelDill = fs.file('$buildDirectory/flutter_runner_patched_sdk/platform_strong.dill');
   final File flutterPatchedSdk = fs.file('$buildDirectory/flutter_runner_patched_sdk');
   final String packages = '$buildDirectory/dartlang/gen/$path/${name}_dart_library.packages';
@@ -91,7 +92,7 @@
     muteCommandLogging: false,
     verboseHelp: false,
     overrides: <Type, Generator>{
-      FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
+      FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig, devFinder: devFinder),
       Artifacts: () => OverrideArtifacts(
         parent: CachedArtifacts(),
         frontendServer: frontendServer,
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index be89010..ed48b22 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -108,12 +108,8 @@
     if (!fuchsiaWorkflow.canListDevices) {
       return <Device>[];
     }
-    final String text = await fuchsiaSdk.netls();
-    final List<FuchsiaDevice> devices = <FuchsiaDevice>[];
-    for (String name in parseFuchsiaDeviceOutput(text)) {
-      final String id = await fuchsiaSdk.netaddr();
-      devices.add(FuchsiaDevice(id, name: name));
-    }
+    final String text = await fuchsiaSdk.listDevices();
+    final List<FuchsiaDevice> devices = parseListDevices(text);
     return devices;
   }
 
@@ -121,24 +117,18 @@
   Future<List<String>> getDiagnostics() async => const <String>[];
 }
 
-/// Parses output from the netls tool into fuchsia devices names.
-///
-/// Example output:
-///     $ ./netls
-///     > device liliac-shore-only-last (fe80::82e4:da4d:fe81:227d/3)
 @visibleForTesting
-List<String> parseFuchsiaDeviceOutput(String text) {
-  final List<String> names = <String>[];
+List<FuchsiaDevice> parseListDevices(String text) {
+  final List<FuchsiaDevice> devices = <FuchsiaDevice>[];
   for (String rawLine in text.trim().split('\n')) {
     final String line = rawLine.trim();
-    if (!line.startsWith('device'))
-      continue;
-    // ['device', 'device name', '(id)']
+    // ['ip', 'device name']
     final List<String> words = line.split(' ');
     final String name = words[1];
-    names.add(name);
+    final String id = words[0];
+    devices.add(FuchsiaDevice(id, name: name));
   }
-  return names;
+  return devices;
 }
 
 class FuchsiaDevice extends Device {
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
index 63f91e1..a9f95a6 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
@@ -24,21 +24,15 @@
 /// This workflow assumes development within the fuchsia source tree,
 /// including a working fx command-line tool in the user's PATH.
 class FuchsiaSdk {
-  static const List<String> _netaddrCommand = <String>['fx', 'netaddr', '--fuchsia', '--nowait'];
-  static const List<String> _netlsCommand = <String>['fx', 'netls', '--nowait'];
   static const List<String> _syslogCommand = <String>['fx', 'syslog', '--clock', 'Local'];
 
-  /// Invokes the `netaddr` command.
-  ///
-  /// This returns the network address of an attached fuchsia device. Does
-  /// not currently support multiple attached devices.
-  ///
   /// Example output:
-  ///     $ fx netaddr --fuchsia --nowait
-  ///     > fe80::9aaa:fcff:fe60:d3af%eth1
-  Future<String> netaddr() async {
+  ///    $ dev_finder list -full
+  ///    > 192.168.42.56 paper-pulp-bush-angel
+  Future<String> listDevices() async {
     try {
-      final RunResult process = await runAsync(_netaddrCommand);
+      final String path = fuchsiaArtifacts.devFinder.absolute.path;
+      final RunResult process = await runAsync(<String>[path, 'list', '-full']);
       return process.stdout.trim();
     } on ArgumentError catch (exception) {
       throwToolExit('$exception');
@@ -69,36 +63,19 @@
     }
     return null;
   }
-
-  /// Invokes the `netls` command.
-  ///
-  /// This lists attached fuchsia devices with their name and address. Does
-  /// not currently support multiple attached devices.
-  ///
-  /// Example output:
-  ///     $ fx netls --nowait
-  ///     > device liliac-shore-only-last (fe80::82e4:da4d:fe81:227d/3)
-  Future<String> netls() async {
-    try {
-      final RunResult process = await runAsync(_netlsCommand);
-      return process.stdout;
-    } on ArgumentError catch (exception) {
-      throwToolExit('$exception');
-    }
-    return null;
-  }
 }
 
 /// Fuchsia-specific artifacts used to interact with a device.
 class FuchsiaArtifacts {
   /// Creates a new [FuchsiaArtifacts].
   ///
-  /// May optionally provide a file `sshConfig` file.
-  FuchsiaArtifacts({File sshConfig})
-    : _sshConfig = sshConfig;
+  /// May optionally provide a file `sshConfig` file and `devFinder` file.
+  FuchsiaArtifacts({File sshConfig, File devFinder})
+    : _sshConfig = sshConfig,
+      _devFinder = devFinder;
 
   /// The location of the SSH configuration file used to interact with a
-  /// fuchsia device.
+  /// Fuchsia device.
   ///
   /// Requires the env variable `BUILD_DIR` to be set if not provided by
   /// the constructor.
@@ -114,4 +91,22 @@
     return _sshConfig;
   }
   File _sshConfig;
+
+  /// The location of the dev finder tool used to locate connected
+  /// Fuchsia devices.
+  ///
+  /// Requires the env variable `BUILD_DIR` to be set if not provided by
+  /// the constructor.
+  File get devFinder {
+    if (_devFinder == null) {
+      final String buildDirectory = platform.environment['BUILD_DIR'];
+      if (buildDirectory == null) {
+        throwToolExit('BUILD_DIR must be supplied to dev_finder. For example:\n'
+          '  export BUILD_DIR=path/to/fuchsia/out/x64\n');
+      }
+      _devFinder = fs.file('$buildDirectory/host_x64/dev_finder');
+    }
+    return _devFinder;
+  }
+  File _devFinder;
 }
diff --git a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
index fb192f0..db1ce50 100644
--- a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
+++ b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart
@@ -29,12 +29,13 @@
       expect(device.name, name);
     });
 
-    test('parse netls log output', () {
-      const String example = 'device lilia-shore-only-last (fe80::0000:a00a:f00f:2002/3)';
-      final List<String> names = parseFuchsiaDeviceOutput(example);
+    test('parse dev_finder output', () {
+      const String example = '192.168.42.56 paper-pulp-bush-angel';
+      final List<FuchsiaDevice> names = parseListDevices(example);
 
       expect(names.length, 1);
-      expect(names.first, 'lilia-shore-only-last');
+      expect(names.first.name, 'paper-pulp-bush-angel');
+      expect(names.first.id, '192.168.42.56');
     });
 
     test('default capabilities', () async {