Add publish-port flag to disable mDNS port discovery (#67452)

diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index f3c520f..7fa8e81 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -55,6 +55,11 @@
   }) {
     requiresPubspecYaml();
     addEnableExperimentation(hide: !verboseHelp);
+
+    // By default, the drive app should not publish the VM service port over mDNS
+    // to prevent a local network permission dialog on iOS 14+,
+    // which cannot be accepted or dismissed in a CI environment.
+    addPublishPort(enabledByDefault: false, verboseHelp: verboseHelp);
     argParser
       ..addFlag('keep-app-running',
         defaultsTo: null,
@@ -215,7 +220,8 @@
             )
             : DebuggingOptions.enabled(
               getBuildInfo(),
-              port: stringArg('web-port')
+              port: stringArg('web-port'),
+              disablePortPublication: disablePortPublication,
             ),
           stayResident: false,
           urlTunneller: null,
@@ -501,6 +507,7 @@
       command.getBuildInfo(),
       startPaused: true,
       hostVmServicePort: webUri != null ? command.hostVmservicePort : 0,
+      disablePortPublication: command.disablePortPublication,
       ddsPort: command.ddsPort,
       verboseSystemLogs: command.verboseSystemLogs,
       cacheSkSL: command.cacheSkSL,
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 63c00d2..092abe1 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -97,6 +97,11 @@
     usesFilesystemOptions(hide: !verboseHelp);
     usesExtraFrontendOptions();
     addEnableExperimentation(hide: !verboseHelp);
+
+    // By default, the app should to publish the VM service port over mDNS.
+    // This will allow subsequent "flutter attach" commands to connect to the VM
+    // without needing to know the port.
+    addPublishPort(enabledByDefault: true, verboseHelp: verboseHelp);
     argParser
       ..addFlag('start-paused',
         negatable: false,
@@ -407,6 +412,7 @@
         purgePersistentCache: purgePersistentCache,
         deviceVmServicePort: deviceVmservicePort,
         hostVmServicePort: hostVmservicePort,
+        disablePortPublication: disablePortPublication,
         ddsPort: ddsPort,
         verboseSystemLogs: boolArg('verbose-system-logs'),
         initializePlatform: boolArg('web-initialize-platform'),
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 769b0ee..92d4ebf 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -813,6 +813,7 @@
     this.useTestFonts = false,
     this.verboseSystemLogs = false,
     this.hostVmServicePort,
+    this.disablePortPublication = false,
     this.deviceVmServicePort,
     this.ddsPort,
     this.initializePlatform = true,
@@ -855,6 +856,7 @@
       purgePersistentCache = false,
       verboseSystemLogs = false,
       hostVmServicePort = null,
+      disablePortPublication = false,
       deviceVmServicePort = null,
       ddsPort = null,
       vmserviceOutFile = null,
@@ -884,6 +886,7 @@
   final bool initializePlatform;
   final int hostVmServicePort;
   final int deviceVmServicePort;
+  final bool disablePortPublication;
   final int ddsPort;
   final String port;
   final String hostname;
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index a7b0abd..e8e053b 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -22,7 +22,6 @@
 import '../device.dart';
 import '../globals.dart' as globals;
 import '../macos/xcode.dart';
-import '../mdns_discovery.dart';
 import '../project.dart';
 import '../protocol_discovery.dart';
 import '../vmservice.dart';
@@ -369,6 +368,7 @@
       '--enable-service-port-fallback',
       '--disable-service-auth-codes',
       '--observatory-port=$assumedObservatoryPort',
+      if (debuggingOptions.disablePortPublication) '--disable-observatory-publication',
       if (debuggingOptions.startPaused) '--start-paused',
       if (dartVmFlags.isNotEmpty) '--dart-flags="$dartVmFlags"',
       if (debuggingOptions.useTestFonts) '--use-test-fonts',
@@ -453,7 +453,6 @@
       _logger.printTrace('Application launched on the device. Waiting for observatory port.');
       final FallbackDiscovery fallbackDiscovery = FallbackDiscovery(
         logger: _logger,
-        mDnsObservatoryDiscovery: MDnsObservatoryDiscovery.instance,
         portForwarder: portForwarder,
         protocolDiscovery: observatoryDiscovery,
         flutterUsage: globals.flutterUsage,
diff --git a/packages/flutter_tools/lib/src/ios/fallback_discovery.dart b/packages/flutter_tools/lib/src/ios/fallback_discovery.dart
index b321114..127c0fc 100644
--- a/packages/flutter_tools/lib/src/ios/fallback_discovery.dart
+++ b/packages/flutter_tools/lib/src/ios/fallback_discovery.dart
@@ -8,7 +8,6 @@
 import '../base/io.dart';
 import '../base/logger.dart';
 import '../device.dart';
-import '../mdns_discovery.dart';
 import '../protocol_discovery.dart';
 import '../reporting/reporting.dart';
 
@@ -17,36 +16,23 @@
 /// A protocol for discovery of a vmservice on an attached iOS device with
 /// multiple fallbacks.
 ///
-/// On versions of iOS 13 and greater, libimobiledevice can no longer listen to
-/// logs directly. The only way to discover an active observatory is through the
-/// mDNS protocol. However, there are a number of circumstances where this breaks
-/// down, such as when the device is connected to certain wifi networks or with
-/// certain hotspot connections enabled.
-///
-/// Another approach to discover a vmservice is to attempt to assign a
+/// First, it tries to discover a vmservice by assigning a
 /// specific port and then attempt to connect. This may fail if the port is
 /// not available. This port value should be either random, or otherwise
 /// generated with application specific input. This reduces the chance of
 /// accidentally connecting to another running flutter application.
 ///
-/// Finally, if neither of the above approaches works, we can still attempt
-/// to parse logs.
-///
-/// To improve the overall resilience of the process, this class combines the
-/// three discovery strategies. First it assigns a port and attempts to connect.
-/// Then if this fails it falls back to mDNS, then finally attempting to scan
-/// logs.
+/// If that does not work, attempt to scan logs from the attached debugger
+/// and parse the connected port logged by the engine.
 class FallbackDiscovery {
   FallbackDiscovery({
     @required DevicePortForwarder portForwarder,
-    @required MDnsObservatoryDiscovery mDnsObservatoryDiscovery,
     @required Logger logger,
     @required ProtocolDiscovery protocolDiscovery,
     @required Usage flutterUsage,
     @required VmServiceConnector vmServiceConnectUri,
     Duration pollingDelay,
   }) : _logger = logger,
-       _mDnsObservatoryDiscovery = mDnsObservatoryDiscovery,
        _portForwarder = portForwarder,
        _protocolDiscovery = protocolDiscovery,
        _flutterUsage = flutterUsage,
@@ -56,7 +42,6 @@
   static const String _kEventName = 'ios-handshake';
 
   final DevicePortForwarder _portForwarder;
-  final MDnsObservatoryDiscovery _mDnsObservatoryDiscovery;
   final Logger _logger;
   final ProtocolDiscovery _protocolDiscovery;
   final Usage _flutterUsage;
@@ -97,37 +82,13 @@
     } on Exception catch (err) {
       _logger.printTrace(err.toString());
     }
-    _logger.printTrace('Failed to connect with log scanning, falling back to mDNS');
+    _logger.printTrace('Failed to connect with log scanning');
     UsageEvent(
       _kEventName,
       'log-failure',
       flutterUsage: _flutterUsage,
     ).send();
 
-    try {
-      final Uri result = await _mDnsObservatoryDiscovery.getObservatoryUri(
-        packageId,
-        device,
-        usesIpv6: usesIpv6,
-        hostVmservicePort: hostVmservicePort,
-      );
-      if (result != null) {
-        UsageEvent(
-          _kEventName,
-          'mdns-success',
-          flutterUsage: _flutterUsage,
-        ).send();
-        return result;
-      }
-    } on Exception catch (err) {
-      _logger.printTrace(err.toString());
-    }
-    _logger.printTrace('Failed to connect with mDNS');
-    UsageEvent(
-      _kEventName,
-      'mdns-failure',
-      flutterUsage: _flutterUsage,
-    ).send();
     return null;
   }
 
@@ -200,7 +161,7 @@
       await Future<void>.delayed(_pollingDelay);
       attempts += 1;
     }
-    _logger.printTrace('Failed to connect directly, falling back to mDNS');
+    _logger.printTrace('Failed to connect directly, falling back to log scanning');
     _sendFailureEvent(firstException, assumedDevicePort);
     return null;
   }
diff --git a/packages/flutter_tools/lib/src/mdns_discovery.dart b/packages/flutter_tools/lib/src/mdns_discovery.dart
index 3375e8d..ac76abd 100644
--- a/packages/flutter_tools/lib/src/mdns_discovery.dart
+++ b/packages/flutter_tools/lib/src/mdns_discovery.dart
@@ -51,6 +51,7 @@
   /// it will return that instance's information regardless of what application
   /// the Observatory instance is for.
   // TODO(jonahwilliams): use `deviceVmservicePort` to filter mdns results.
+  @visibleForTesting
   Future<MDnsObservatoryDiscoveryResult> query({String applicationId, int deviceVmservicePort}) async {
     globals.printTrace('Checking for advertised Dart observatories...');
     try {
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 1e3ab0f..7003262 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -369,6 +369,17 @@
     return null;
   }
 
+  void addPublishPort({ bool enabledByDefault = true, bool verboseHelp = false }) {
+    argParser.addFlag('publish-port',
+        negatable: true,
+        hide: !verboseHelp,
+        help: 'Publish the VM service port over mDNS. Disable to prevent the'
+            'local network permission app dialog in debug and profile build modes (iOS devices only.)',
+        defaultsTo: enabledByDefault);
+  }
+
+  bool get disablePortPublication => !boolArg('publish-port');
+
   void usesIpv6Flag() {
     argParser.addFlag(ipv6Flag,
       hide: true,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
index fa739fa..48930cc 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
@@ -708,6 +708,11 @@
         '--purge-persistent-cache',
         () => debuggingOptions.purgePersistentCache,
       );
+
+      testOptionThatDefaultsToFalse(
+        '--publish-port',
+            () => !debuggingOptions.disablePortPublication,
+      );
     });
   });
 
diff --git a/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart b/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart
index 3b33719..ff7db18 100644
--- a/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart
@@ -7,7 +7,6 @@
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/device.dart';
 import 'package:flutter_tools/src/ios/fallback_discovery.dart';
-import 'package:flutter_tools/src/mdns_discovery.dart';
 import 'package:flutter_tools/src/protocol_discovery.dart';
 import 'package:flutter_tools/src/reporting/reporting.dart';
 import 'package:mockito/mockito.dart';
@@ -19,7 +18,6 @@
 void main() {
   BufferLogger logger;
   FallbackDiscovery fallbackDiscovery;
-  MockMDnsObservatoryDiscovery mockMDnsObservatoryDiscovery;
   MockPrototcolDiscovery mockPrototcolDiscovery;
   MockPortForwarder mockPortForwarder;
   MockVmService mockVmService;
@@ -30,12 +28,10 @@
       outputPreferences: OutputPreferences.test(),
     );
     mockVmService = MockVmService();
-    mockMDnsObservatoryDiscovery = MockMDnsObservatoryDiscovery();
     mockPrototcolDiscovery = MockPrototcolDiscovery();
     mockPortForwarder = MockPortForwarder();
     fallbackDiscovery = FallbackDiscovery(
       logger: logger,
-      mDnsObservatoryDiscovery: mockMDnsObservatoryDiscovery,
       portForwarder: mockPortForwarder,
       protocolDiscovery: mockPrototcolDiscovery,
       flutterUsage: Usage.test(),
@@ -94,7 +90,7 @@
     ), Uri.parse('http://localhost:1'));
   });
 
-  testWithoutContext('Selects mdns discovery if VM service connecton fails due to Sentinel', () async {
+  testWithoutContext('Selects log scanning if VM service connecton fails due to Sentinel', () async {
     when(mockVmService.getVM()).thenAnswer((Invocation invocation) async {
       return VM.parse(<String, Object>{})..isolates = <IsolateRef>[
         IsolateRef(
@@ -106,57 +102,8 @@
     });
     when(mockVmService.getIsolate(any))
       .thenThrow(SentinelException.parse('Something', <String, dynamic>{}));
-    when(mockMDnsObservatoryDiscovery.getObservatoryUri(
-      'hello',
-      null, // Device
-      usesIpv6: false,
-      hostVmservicePort: 1,
-    )).thenAnswer((Invocation invocation) async {
-      return Uri.parse('http://localhost:1234');
-    });
-
-    expect(await fallbackDiscovery.discover(
-      assumedDevicePort: 23,
-      device: null,
-      hostVmservicePort: 1,
-      packageId: 'hello',
-      usesIpv6: false,
-       packageName: 'hello',
-    ), Uri.parse('http://localhost:1234'));
-  });
-
-  testWithoutContext('Selects mdns discovery if VM service connecton fails', () async {
-    when(mockVmService.getVM()).thenThrow(Exception());
-
-    when(mockMDnsObservatoryDiscovery.getObservatoryUri(
-      'hello',
-      null, // Device
-      usesIpv6: false,
-      hostVmservicePort: 1,
-    )).thenAnswer((Invocation invocation) async {
-      return Uri.parse('http://localhost:1234');
-    });
-
-    expect(await fallbackDiscovery.discover(
-      assumedDevicePort: 23,
-      device: null,
-      hostVmservicePort: 1,
-      packageId: 'hello',
-      usesIpv6: false,
-       packageName: 'hello',
-    ), Uri.parse('http://localhost:1234'));
-  });
-
-  testWithoutContext('Selects log scanning if both VM Service and mDNS fails', () async {
-    when(mockVmService.getVM()).thenThrow(Exception());
-    when(mockMDnsObservatoryDiscovery.getObservatoryUri(
-      'hello',
-      null, // Device
-      usesIpv6: false,
-      hostVmservicePort: 1,
-    )).thenThrow(Exception());
     when(mockPrototcolDiscovery.uri).thenAnswer((Invocation invocation) async {
-      return Uri.parse('http://localhost:5678');
+      return Uri.parse('http://localhost:1234');
     });
 
     expect(await fallbackDiscovery.discover(
@@ -165,12 +112,42 @@
       hostVmservicePort: 1,
       packageId: 'hello',
       usesIpv6: false,
+       packageName: 'hello',
+    ), Uri.parse('http://localhost:1234'));
+  });
+
+  testWithoutContext('Selects log scanning if VM service connecton fails', () async {
+    when(mockVmService.getVM()).thenThrow(Exception());
+
+    when(mockPrototcolDiscovery.uri).thenAnswer((Invocation invocation) async {
+      return Uri.parse('http://localhost:1234');
+    });
+
+    expect(await fallbackDiscovery.discover(
+      assumedDevicePort: 23,
+      device: null,
+      hostVmservicePort: 1,
+      packageId: 'hello',
+      usesIpv6: false,
+       packageName: 'hello',
+    ), Uri.parse('http://localhost:1234'));
+  });
+
+  testWithoutContext('Fails if both VM Service and log scanning fails', () async {
+    when(mockVmService.getVM()).thenThrow(Exception());
+    when(mockPrototcolDiscovery.uri).thenAnswer((Invocation invocation) async => null);
+
+    expect(await fallbackDiscovery.discover(
+      assumedDevicePort: 23,
+      device: null,
+      hostVmservicePort: 1,
+      packageId: 'hello',
+      usesIpv6: false,
       packageName: 'hello',
-    ), Uri.parse('http://localhost:5678'));
+    ), isNull);
   });
 }
 
-class MockMDnsObservatoryDiscovery extends Mock implements MDnsObservatoryDiscovery {}
 class MockPrototcolDiscovery extends Mock implements ProtocolDiscovery {}
 class MockPortForwarder extends Mock implements DevicePortForwarder {}
 class MockVmService extends Mock implements VmService {}
diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart
index 85cfd78..8035163 100644
--- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart
@@ -21,7 +21,6 @@
 import 'package:flutter_tools/src/ios/ios_deploy.dart';
 import 'package:flutter_tools/src/ios/iproxy.dart';
 import 'package:flutter_tools/src/ios/mac.dart';
-import 'package:flutter_tools/src/mdns_discovery.dart';
 import 'package:flutter_tools/src/reporting/reporting.dart';
 import 'package:mockito/mockito.dart';
 import 'package:vm_service/vm_service.dart';
@@ -123,63 +122,7 @@
     verify(devicePortForwarder.dispose()).called(1);
   });
 
-  // Still uses context for analytics and mDNS.
-  testUsingContext('IOSDevice.startApp succeeds in debug mode via mDNS discovery when log reading fails', () async {
-    final FileSystem fileSystem = MemoryFileSystem.test();
-    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
-      kDeployCommand,
-      kAttachDebuggerCommand,
-    ]);
-    final IOSDevice device = setUpIOSDevice(
-      processManager: processManager,
-      fileSystem: fileSystem,
-      vmServiceConnector: (String string, {Log log}) async {
-        throw const io.SocketException(
-          'OS Error: Connection refused, errno = 61, address = localhost, port '
-          '= 58943',
-        );
-      },
-    );
-    final IOSApp iosApp = PrebuiltIOSApp(
-      projectBundleId: 'app',
-      bundleName: 'Runner',
-      bundleDir: fileSystem.currentDirectory,
-    );
-    final Uri uri = Uri(
-      scheme: 'http',
-      host: '127.0.0.1',
-      port: 1234,
-      path: 'observatory',
-    );
-
-    device.portForwarder = const NoOpDevicePortForwarder();
-    device.setLogReader(iosApp, FakeDeviceLogReader());
-
-    when(MDnsObservatoryDiscovery.instance.getObservatoryUri(
-      any,
-      any,
-      usesIpv6: anyNamed('usesIpv6'),
-      hostVmservicePort: anyNamed('hostVmservicePort')
-    )).thenAnswer((Invocation invocation) async => uri);
-
-    final LaunchResult launchResult = await device.startApp(iosApp,
-      prebuiltApplication: true,
-      debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
-      platformArgs: <String, dynamic>{},
-      fallbackPollingDelay: Duration.zero,
-      fallbackThrottleTimeout: const Duration(milliseconds: 10),
-    );
-
-    verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-success')).called(1);
-    expect(launchResult.started, true);
-    expect(launchResult.hasObservatory, true);
-    expect(await device.stopApp(iosApp), false);
-  }, overrides: <Type, Generator>{
-    MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
-    Usage: () => MockUsage(),
-  });
-
-  // Still uses context for analytics and mDNS.
+  // Still uses context for analytics.
   testUsingContext('IOSDevice.startApp attaches in debug mode via log reading on iOS 13+', () async {
     final FileSystem fileSystem = MemoryFileSystem.test();
     final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
@@ -223,14 +166,12 @@
     expect(launchResult.started, true);
     expect(launchResult.hasObservatory, true);
     verify(globals.flutterUsage.sendEvent('ios-handshake', 'log-success')).called(1);
-    verifyNever(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure'));
     expect(await device.stopApp(iosApp), false);
   }, overrides: <Type, Generator>{
-    MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
     Usage: () => MockUsage(),
   });
 
-  // Still uses context for analytics and mDNS.
+  // Still uses context for analytics.
   testUsingContext('IOSDevice.startApp launches in debug mode via log reading on <iOS 13', () async {
     final FileSystem fileSystem = MemoryFileSystem.test();
     final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
@@ -275,14 +216,12 @@
     expect(launchResult.started, true);
     expect(launchResult.hasObservatory, true);
     verify(globals.flutterUsage.sendEvent('ios-handshake', 'log-success')).called(1);
-    verifyNever(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure'));
     expect(await device.stopApp(iosApp), false);
   }, overrides: <Type, Generator>{
-    MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
     Usage: () => MockUsage(),
   });
 
-  // Still uses context for analytics and mDNS.
+  // Still uses context for analytics.
   testUsingContext('IOSDevice.startApp fails in debug mode when Observatory URI is malformed', () async {
     final FileSystem fileSystem = MemoryFileSystem.test();
     final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
@@ -332,9 +271,7 @@
       value: anyNamed('value'),
     )).called(1);
     verify(globals.flutterUsage.sendEvent('ios-handshake', 'log-failure')).called(1);
-    verify(globals.flutterUsage.sendEvent('ios-handshake', 'mdns-failure')).called(1);
     }, overrides: <Type, Generator>{
-      MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
       Usage: () => MockUsage(),
     });
 
@@ -371,7 +308,7 @@
     Usage: () => MockUsage(),
   });
 
-  // Still uses context for analytics and mDNS.
+  // Still uses context for analytics.
   testUsingContext('IOSDevice.startApp forwards all supported debugging options', () async {
     final FileSystem fileSystem = MemoryFileSystem.test();
     final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
@@ -397,6 +334,7 @@
             '--enable-service-port-fallback',
             '--disable-service-auth-codes',
             '--observatory-port=60700',
+            '--disable-observatory-publication',
             '--start-paused',
             '--dart-flags="--foo,--null_assertions"',
             '--enable-checked-mode',
@@ -449,6 +387,7 @@
         BuildInfo.debug,
         startPaused: true,
         disableServiceAuthCodes: true,
+        disablePortPublication: true,
         dartFlags: '--foo',
         enableSoftwareRendering: true,
         skiaDeterministicRendering: true,
@@ -470,7 +409,6 @@
     expect(await device.stopApp(iosApp), false);
     expect(processManager.hasRemainingExpectations, false);
   }, overrides: <Type, Generator>{
-    MDnsObservatoryDiscovery: () => MockMDnsObservatoryDiscovery(),
     Usage: () => MockUsage(),
   });
 }
@@ -525,6 +463,5 @@
 class MockDevicePortForwarder extends Mock implements DevicePortForwarder {}
 class MockDeviceLogReader extends Mock implements DeviceLogReader  {}
 class MockUsage extends Mock implements Usage {}
-class MockMDnsObservatoryDiscovery extends Mock implements MDnsObservatoryDiscovery {}
 class MockVmService extends Mock implements VmService {}
 class MockDartDevelopmentService extends Mock implements DartDevelopmentService {}