Skip to content

Commit ecabb1a

Browse files
authored
Hide error on mDNS registration failure and print warning in flutter attach (flutter#166782)
Publishing an mDNS service stopped working on macOS 15.4 for the iOS Simulator. `DNSServiceRegister` always returns a callback with error code `kDNSServiceErr_PolicyDenied`, as if the user had rejected the permissions popup (except no popup is ever shown). mDNS is used to discover the Dart VM when running `flutter attach`. This PR does not display the error message when `DNSServiceRegister` fails for iOS Simulators. Instead, it prints a warning if `flutter attach` takes too long with instructions to either use `--debug-url` or to run `flutter attach` before running the app. If `DNSServiceRegister` works again in a future macOS update, mDNS will continue to work the way it did previously. Workaround for flutter#166333. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent fbed8a7 commit ecabb1a

File tree

4 files changed

+148
-6
lines changed

4 files changed

+148
-6
lines changed

engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterDartVMServicePublisher.mm

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ - (void)stopService {
7676
}
7777

7878
- (void)publishServiceProtocolPort:(NSURL*)url {
79-
// TODO(vashworth): Remove once done debugging https://github.com/flutter/flutter/issues/129836
80-
FML_LOG(INFO) << "Publish Service Protocol Port";
8179
DNSServiceFlags flags = kDNSServiceFlagsDefault;
8280
#if TARGET_IPHONE_SIMULATOR
8381
// Simulator needs to use local loopback explicitly to work.
@@ -126,10 +124,20 @@ static void DNSSD_API RegistrationCallback(DNSServiceRef sdRef,
126124
if (errorCode == kDNSServiceErr_NoError) {
127125
FML_DLOG(INFO) << "FlutterDartVMServicePublisher is ready!";
128126
} else if (errorCode == kDNSServiceErr_PolicyDenied) {
127+
// Local Network permissions on simulators stopped working in macOS 15.4 and will always return
128+
// kDNSServiceErr_PolicyDenied. See
129+
// https://github.com/flutter/flutter/issues/166333#issuecomment-2786720560.
130+
#if TARGET_IPHONE_SIMULATOR
131+
FML_DLOG(WARNING)
132+
<< "Could not register as server for FlutterDartVMServicePublisher, permission "
133+
<< "denied. Check your 'Local Network' permissions for this app in the Privacy section of "
134+
<< "the system Settings.";
135+
#else // TARGET_IPHONE_SIMULATOR
129136
FML_LOG(ERROR)
130137
<< "Could not register as server for FlutterDartVMServicePublisher, permission "
131138
<< "denied. Check your 'Local Network' permissions for this app in the Privacy section of "
132139
<< "the system Settings.";
140+
#endif // TARGET_IPHONE_SIMULATOR
133141
} else {
134142
FML_LOG(ERROR) << "Could not register as server for FlutterDartVMServicePublisher. Check your "
135143
"network settings and relaunch the application.";

engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,6 @@ - (BOOL)createShell:(NSString*)entrypoint
867867
FML_LOG(ERROR) << "Could not start a shell FlutterEngine with entrypoint: "
868868
<< entrypoint.UTF8String;
869869
} else {
870-
// TODO(vashworth): Remove once done debugging https://github.com/flutter/flutter/issues/129836
871-
FML_LOG(INFO) << "Enabled VM Service Publication: " << settings.enable_vm_service_publication;
872870
[self setUpShell:std::move(shell)
873871
withVMServicePublication:settings.enable_vm_service_publication];
874872
if ([FlutterEngine isProfilerEnabled]) {

packages/flutter_tools/lib/src/commands/attach.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import '../device.dart';
2323
import '../device_port_forwarder.dart';
2424
import '../device_vm_service_discovery_for_attach.dart';
2525
import '../ios/devices.dart';
26+
import '../ios/simulators.dart';
2627
import '../macos/macos_ipad_device.dart';
2728
import '../mdns_discovery.dart';
2829
import '../project.dart';
@@ -316,8 +317,20 @@ known, it can be explicitly provided to attach via the command-line, e.g.
316317
final Status discoveryStatus = _logger.startSpinner(
317318
timeout: const Duration(seconds: 30),
318319
slowWarningCallback: () {
319-
// On iOS we rely on mDNS to find Dart VM Service. Remind the user to allow local network permissions on the device.
320-
if (_isIOSDevice(device)) {
320+
// On iOS we rely on mDNS to find Dart VM Service.
321+
if (device is IOSSimulator) {
322+
// mDNS on simulators stopped working in macOS 15.4.
323+
// See https://github.com/flutter/flutter/issues/166333.
324+
return 'The Dart VM Service was not discovered after 30 seconds. '
325+
'This may be due to limited mDNS support in the iOS Simulator.\n\n'
326+
'Click "Allow" to the prompt on your device asking if you would like to find and connect devices on your local network. '
327+
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
328+
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again.\n\n"
329+
'If you do not receive a prompt, either run "flutter attach" before starting the '
330+
'app or use the Dart VM service URL from the Xcode console with '
331+
'"flutter attach --debug-url=<URL>".\n';
332+
} else if (_isIOSDevice(device)) {
333+
// Remind the user to allow local network permissions on the device.
321334
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n\n'
322335
'Click "Allow" to the prompt on your device asking if you would like to find and connect devices on your local network. '
323336
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
@@ -326,6 +339,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
326339

327340
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n';
328341
},
342+
warningColor: TerminalColor.cyan,
329343
);
330344

331345
vmServiceUri = vmServiceDiscovery.uris;

packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:fake_async/fake_async.dart';
78
import 'package:file/memory.dart';
89
import 'package:flutter_tools/src/android/android_device.dart';
910
import 'package:flutter_tools/src/application_package.dart';
@@ -25,6 +26,7 @@ import 'package:flutter_tools/src/device_port_forwarder.dart';
2526
import 'package:flutter_tools/src/device_vm_service_discovery_for_attach.dart';
2627
import 'package:flutter_tools/src/ios/application_package.dart';
2728
import 'package:flutter_tools/src/ios/devices.dart';
29+
import 'package:flutter_tools/src/ios/simulators.dart';
2830
import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
2931
import 'package:flutter_tools/src/mdns_discovery.dart';
3032
import 'package:flutter_tools/src/project.dart';
@@ -1528,6 +1530,52 @@ void main() {
15281530
DeviceManager: () => testDeviceManager,
15291531
},
15301532
);
1533+
1534+
group('prints warning when too slow', () {
1535+
late SlowWarningCallbackBufferLogger logger;
1536+
1537+
setUp(() {
1538+
logger = SlowWarningCallbackBufferLogger.test();
1539+
});
1540+
1541+
testUsingContext(
1542+
'to find on iOS Simulator',
1543+
() async {
1544+
final FakeIOSSimulator device = FakeIOSSimulator();
1545+
testDeviceManager.devices = <Device>[device];
1546+
FakeAsync().run((FakeAsync fakeAsync) {
1547+
createTestCommandRunner(
1548+
AttachCommand(
1549+
stdio: stdio,
1550+
logger: logger,
1551+
terminal: terminal,
1552+
signals: signals,
1553+
platform: platform,
1554+
processInfo: processInfo,
1555+
fileSystem: testFileSystem,
1556+
),
1557+
).run(<String>['attach']);
1558+
1559+
logger.expectedWarning =
1560+
'The Dart VM Service was not discovered after 30 seconds. '
1561+
'This may be due to limited mDNS support in the iOS Simulator.\n\n'
1562+
'Click "Allow" to the prompt on your device asking if you would like to find and connect devices on your local network. '
1563+
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
1564+
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again.\n\n"
1565+
'If you do not receive a prompt, either run "flutter attach" before starting the '
1566+
'app or use the Dart VM service URL from the Xcode console with '
1567+
'"flutter attach --debug-url=<URL>".\n';
1568+
fakeAsync.elapse(const Duration(seconds: 30));
1569+
});
1570+
},
1571+
overrides: <Type, Generator>{
1572+
FileSystem: () => testFileSystem,
1573+
ProcessManager: () => FakeProcessManager.any(),
1574+
Logger: () => logger,
1575+
DeviceManager: () => testDeviceManager,
1576+
},
1577+
);
1578+
});
15311579
});
15321580
}
15331581

@@ -1966,6 +2014,62 @@ class FakeIOSDevice extends Fake implements IOSDevice {
19662014
}
19672015
}
19682016

2017+
class FakeIOSSimulator extends Fake implements IOSSimulator {
2018+
@override
2019+
final String name = 'name';
2020+
2021+
@override
2022+
String get displayName => name;
2023+
2024+
@override
2025+
bool isSupported() => true;
2026+
2027+
@override
2028+
bool isSupportedForProject(FlutterProject flutterProject) => true;
2029+
2030+
@override
2031+
bool get isConnected => true;
2032+
2033+
@override
2034+
DeviceConnectionInterface get connectionInterface => DeviceConnectionInterface.attached;
2035+
2036+
@override
2037+
bool get ephemeral => true;
2038+
2039+
@override
2040+
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
2041+
2042+
@override
2043+
final PlatformType platformType = PlatformType.ios;
2044+
2045+
@override
2046+
bool get isWirelesslyConnected => false;
2047+
2048+
@override
2049+
DevicePortForwarder portForwarder = RecordingPortForwarder();
2050+
2051+
@override
2052+
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
2053+
String? appId,
2054+
String? fuchsiaModule,
2055+
int? filterDevicePort,
2056+
int? expectedHostPort,
2057+
required bool ipv6,
2058+
required Logger logger,
2059+
}) {
2060+
final MdnsVMServiceDiscoveryForAttach mdnsVMServiceDiscoveryForAttach =
2061+
MdnsVMServiceDiscoveryForAttach(
2062+
device: this,
2063+
appId: appId,
2064+
deviceVmservicePort: filterDevicePort,
2065+
hostVmservicePort: expectedHostPort,
2066+
usesIpv6: ipv6,
2067+
useDeviceIPAsHost: isWirelesslyConnected,
2068+
);
2069+
return mdnsVMServiceDiscoveryForAttach;
2070+
}
2071+
}
2072+
19692073
class FakeMDnsClient extends Fake implements MDnsClient {
19702074
FakeMDnsClient(
19712075
this.ptrRecords,
@@ -2054,3 +2158,21 @@ class FakeTerminal extends Fake implements AnsiTerminal {
20542158
@override
20552159
Stream<String> get keystrokes => StreamController<String>().stream;
20562160
}
2161+
2162+
class SlowWarningCallbackBufferLogger extends BufferLogger {
2163+
SlowWarningCallbackBufferLogger.test() : super.test();
2164+
2165+
String? expectedWarning;
2166+
2167+
@override
2168+
Status startSpinner({
2169+
VoidCallback? onFinish,
2170+
Duration? timeout,
2171+
SlowWarningCallback? slowWarningCallback,
2172+
TerminalColor? warningColor,
2173+
}) {
2174+
expect(slowWarningCallback, isNotNull);
2175+
expect(slowWarningCallback!(), expectedWarning);
2176+
return SilentStatus(stopwatch: Stopwatch(), onFinish: onFinish)..start();
2177+
}
2178+
}

0 commit comments

Comments
 (0)