Flutter Engine
 
Loading...
Searching...
No Matches
FlutterDartVMServicePublisher.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#define FML_USED_ON_EMBEDDER
6
8
9#if FLUTTER_RELEASE
10
12- (instancetype)initWithEnableVMServicePublication:(BOOL)enableVMServicePublication {
13 return [super init];
14}
15@end
16
17#else // FLUTTER_RELEASE
18
19#import <TargetConditionals.h>
20// NSNetService works fine on physical devices before iOS 13.2.
21// However, it doesn't expose the services to regular mDNS
22// queries on the Simulator or on iOS 13.2+ devices.
23//
24// When debugging issues with this implementation, the following is helpful:
25//
26// 1) Running `dns-sd -Z _dartVmService`. This is a built-in macOS tool that
27// can find advertized observatories using this method. If dns-sd can't find
28// it, then the VM service is not getting advertised over any network
29// interface that the host machine has access to.
30// 2) The Python zeroconf package. The dns-sd tool can sometimes see things
31// that aren't advertizing over a network interface - for example, simulators
32// using NSNetService has been observed using dns-sd, but doesn't show up in
33// the Python package (which is a high quality socket based implementation).
34// If that happens, this code should be tweaked such that it shows up in both
35// dns-sd's output and Python zeroconf's detection.
36// 3) The Dart multicast_dns package, which is what Flutter uses to find the
37// port and auth code. If the advertizement shows up in dns-sd and Python
38// zeroconf but not multicast_dns, then it is a bug in multicast_dns.
39#include <dns_sd.h>
40#include <net/if.h>
41
42#include "flutter/fml/logging.h"
45#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
47
49
50@protocol FlutterDartVMServicePublisherDelegate
51- (void)publishServiceProtocolPort:(NSURL*)uri;
52- (void)stopService;
53@end
54
56+ (NSData*)createTxtData:(NSURL*)url;
57
58@property(readonly, class) NSString* serviceName;
59@property(readonly) NSObject<FlutterDartVMServicePublisherDelegate>* delegate;
60@property(nonatomic, readwrite) NSURL* url;
61@property(readonly) BOOL enableVMServicePublication;
62
63@end
64
66@end
67
68@implementation DartVMServiceDNSServiceDelegate {
69 DNSServiceRef _dnsServiceRef;
70}
71
72- (void)stopService {
73 if (_dnsServiceRef) {
74 DNSServiceRefDeallocate(_dnsServiceRef);
75 _dnsServiceRef = NULL;
76 }
77}
78
79- (void)publishServiceProtocolPort:(NSURL*)url {
80 DNSServiceFlags flags = kDNSServiceFlagsDefault;
81#if TARGET_IPHONE_SIMULATOR
82 // Simulator needs to use local loopback explicitly to work.
83 uint32_t interfaceIndex = if_nametoindex("lo0");
84#else // TARGET_IPHONE_SIMULATOR
85 // Physical devices need to request all interfaces.
86 uint32_t interfaceIndex = 0;
87#endif // TARGET_IPHONE_SIMULATOR
88 const char* registrationType = "_dartVmService._tcp";
89
90 const char* domain = "local."; // default domain
91 uint16_t port = [[url port] unsignedShortValue];
92
93 NSData* txtData = [FlutterDartVMServicePublisher createTxtData:url];
94 int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex,
95 FlutterDartVMServicePublisher.serviceName.UTF8String,
96 registrationType, domain, NULL, htons(port), txtData.length,
97 txtData.bytes, RegistrationCallback, NULL);
98
99 if (err == 0) {
100 DNSServiceSetDispatchQueue(_dnsServiceRef, dispatch_get_main_queue());
101 return;
102 }
103
104 NSString* errorMessage = [NSString
105 stringWithFormat:@"Failed to register Dart VM Service port with mDNS with error %d.", err];
106 [FlutterLogger logError:errorMessage];
107 if (@available(iOS 14.0, *)) {
108 errorMessage = [NSString
109 stringWithFormat:@"On iOS 14+, local network broadcast in apps need to be declared in "
110 "the app's Info.plist. Debug and profile Flutter apps and modules host "
111 "VM services on the local network to support debugging features such "
112 "as hot reload and DevTools. To make your Flutter app or module "
113 "attachable and debuggable, add a '%s' value to the 'NSBonjourServices' "
114 "key in your Info.plist for the Debug/Profile configurations. For more "
115 "information, see https://docs.flutter.dev/development/add-to-app/ios/"
116 "project-setup#local-network-privacy-permissions",
117 registrationType];
118 [FlutterLogger logError:errorMessage];
119 }
120}
121
122static void DNSSD_API RegistrationCallback(DNSServiceRef sdRef,
123 DNSServiceFlags flags,
124 DNSServiceErrorType errorCode,
125 const char* name,
126 const char* regType,
127 const char* domain,
128 void* context) {
129 if (errorCode == kDNSServiceErr_NoError) {
130 FML_DLOG(INFO) << "FlutterDartVMServicePublisher is ready!";
131 } else if (errorCode == kDNSServiceErr_PolicyDenied) {
132 // Local Network permissions on simulators stopped working in macOS 15.4 and will always return
133 // kDNSServiceErr_PolicyDenied. See
134 // https://github.com/flutter/flutter/issues/166333#issuecomment-2786720560.
135#if TARGET_IPHONE_SIMULATOR
136 FML_DLOG(WARNING)
137 << "Could not register as server for FlutterDartVMServicePublisher, permission "
138 << "denied. Check your 'Local Network' permissions for this app in the Privacy section of "
139 << "the system Settings.";
140#else // TARGET_IPHONE_SIMULATOR
141 [FlutterLogger logError:@"Could not register as server for FlutterDartVMServicePublisher, "
142 "permission denied. Check your 'Local Network' permissions for this "
143 "app in the Privacy section of the system Settings."];
144#endif // TARGET_IPHONE_SIMULATOR
145 } else {
146 [FlutterLogger logError:@"Could not register as server for FlutterDartVMServicePublisher. "
147 "Check your network settings and relaunch the application."];
148 }
149}
150
151@end
152
153@implementation FlutterDartVMServicePublisher {
155}
156
157- (instancetype)initWithEnableVMServicePublication:(BOOL)enableVMServicePublication {
158 self = [super init];
159 NSAssert(self, @"Super must not return null on init.");
160
161 _delegate = [[DartVMServiceDNSServiceDelegate alloc] init];
162 _enableVMServicePublication = enableVMServicePublication;
163 __weak __typeof(self) weakSelf = self;
164
166
168 [weakSelf, runner = fml::MessageLoop::GetCurrent().GetTaskRunner()](const std::string& uri) {
169 if (!uri.empty()) {
170 runner->PostTask([weakSelf, uri]() {
171 FlutterDartVMServicePublisher* strongSelf = weakSelf;
172 // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port
173 // number.
174 if (strongSelf) {
175 NSURL* url =
176 [[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]];
177 strongSelf.url = url;
178 if (strongSelf.enableVMServicePublication) {
179 [[strongSelf delegate] publishServiceProtocolPort:url];
180 }
181 }
182 });
183 }
184 });
185
186 return self;
187}
188
189+ (NSString*)serviceName {
190 return NSBundle.mainBundle.bundleIdentifier;
191}
192
193+ (NSData*)createTxtData:(NSURL*)url {
194 // Check to see if there's an authentication code. If there is, we'll provide
195 // it as a txt record so flutter tools can establish a connection.
196 NSString* path = [[url path] substringFromIndex:MIN(1, [[url path] length])];
197 NSData* pathData = [path dataUsingEncoding:NSUTF8StringEncoding];
198 NSDictionary<NSString*, NSData*>* txtDict = @{
199 @"authCode" : pathData,
200 };
201 return [NSNetService dataFromTXTRecordDictionary:txtDict];
202}
203
204- (void)dealloc {
205 [_delegate stopService];
206
208}
209@end
210
211#endif // FLUTTER_RELEASE
static CallbackHandle AddServerStatusCallback(const DartVMServiceServerStateCallback &callback)
Add a callback that will get invoked when the VM Service starts up. If the VM Service has already sta...
static bool RemoveServerStatusCallback(CallbackHandle handle)
Removed a callback previously registered via AddServiceStatusCallback.
static void EnsureInitializedForCurrentThread()
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
#define FML_DLOG(severity)
Definition logging.h:121
const char * name
Definition fuchsia.cc:49
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
int BOOL