Flutter Engine
The Flutter Engine
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"
43#include "flutter/fml/message_loop.h"
44#include "flutter/fml/platform/darwin/scoped_nsobject.h"
45#include "flutter/runtime/dart_service_isolate.h"
46#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
47
49
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 // TODO(vashworth): Remove once done debugging https://github.com/flutter/flutter/issues/129836
81 FML_LOG(INFO) << "Publish Service Protocol Port";
82 DNSServiceFlags flags = kDNSServiceFlagsDefault;
83#if TARGET_IPHONE_SIMULATOR
84 // Simulator needs to use local loopback explicitly to work.
85 uint32_t interfaceIndex = if_nametoindex("lo0");
86#else // TARGET_IPHONE_SIMULATOR
87 // Physical devices need to request all interfaces.
88 uint32_t interfaceIndex = 0;
89#endif // TARGET_IPHONE_SIMULATOR
90 const char* registrationType = "_dartVmService._tcp";
91
92 const char* domain = "local."; // default domain
93 uint16_t port = [[url port] unsignedShortValue];
94
95 NSData* txtData = [FlutterDartVMServicePublisher createTxtData:url];
96 int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex,
98 registrationType, domain, NULL, htons(port), txtData.length,
99 txtData.bytes, RegistrationCallback, NULL);
100
101 if (err == 0) {
102 DNSServiceSetDispatchQueue(_dnsServiceRef, dispatch_get_main_queue());
103 return;
104 }
105
106 FML_LOG(ERROR) << "Failed to register Dart VM Service port with mDNS with error " << err << ".";
107 if (@available(iOS 14.0, *)) {
108 FML_LOG(ERROR) << "On iOS 14+, local network broadcast in apps need to be declared in "
109 << "the app's Info.plist. Debug and profile Flutter apps and modules host "
110 << "VM services on the local network to support debugging features such "
111 << "as hot reload and DevTools. To make your Flutter app or module "
112 << "attachable and debuggable, add a '" << registrationType << "' value "
113 << "to the 'NSBonjourServices' key in your Info.plist for the Debug/"
114 << "Profile configurations. " << "For more information, see "
115 << "https://flutter.dev/docs/development/add-to-app/ios/"
116 "project-setup#local-network-privacy-permissions";
117 }
118}
119
120static void DNSSD_API RegistrationCallback(DNSServiceRef sdRef,
121 DNSServiceFlags flags,
122 DNSServiceErrorType errorCode,
123 const char* name,
124 const char* regType,
125 const char* domain,
126 void* context) {
127 if (errorCode == kDNSServiceErr_NoError) {
128 FML_DLOG(INFO) << "FlutterDartVMServicePublisher is ready!";
129 } else if (errorCode == kDNSServiceErr_PolicyDenied) {
131 << "Could not register as server for FlutterDartVMServicePublisher, permission "
132 << "denied. Check your 'Local Network' permissions for this app in the Privacy section of "
133 << "the system Settings.";
134 } else {
135 FML_LOG(ERROR) << "Could not register as server for FlutterDartVMServicePublisher. Check your "
136 "network settings and relaunch the application.";
137 }
138}
139
140@end
141
142@implementation FlutterDartVMServicePublisher {
144}
145
146- (instancetype)initWithEnableVMServicePublication:(BOOL)enableVMServicePublication {
147 self = [super init];
148 NSAssert(self, @"Super must not return null on init.");
149
150 _delegate = [[DartVMServiceDNSServiceDelegate alloc] init];
151 _enableVMServicePublication = enableVMServicePublication;
152 __weak __typeof(self) weakSelf = self;
153
155
157 [weakSelf, runner = fml::MessageLoop::GetCurrent().GetTaskRunner()](const std::string& uri) {
158 if (!uri.empty()) {
159 runner->PostTask([weakSelf, uri]() {
160 FlutterDartVMServicePublisher* strongSelf = weakSelf;
161 // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port
162 // number.
163 if (strongSelf) {
164 NSURL* url =
165 [[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]];
166 strongSelf.url = url;
167 if (strongSelf.enableVMServicePublication) {
168 [[strongSelf delegate] publishServiceProtocolPort:url];
169 }
170 }
171 });
172 }
173 });
174
175 return self;
176}
177
178+ (NSString*)serviceName {
179 return NSBundle.mainBundle.bundleIdentifier;
180}
181
182+ (NSData*)createTxtData:(NSURL*)url {
183 // Check to see if there's an authentication code. If there is, we'll provide
184 // it as a txt record so flutter tools can establish a connection.
185 NSString* path = [[url path] substringFromIndex:MIN(1, [[url path] length])];
186 NSData* pathData = [path dataUsingEncoding:NSUTF8StringEncoding];
187 NSDictionary<NSString*, NSData*>* txtDict = @{
188 @"authCode" : pathData,
189 };
190 return [NSNetService dataFromTXTRecordDictionary:txtDict];
191}
192
193- (void)dealloc {
194 [_delegate stopService];
195
197}
198@end
199
200#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()
Definition: message_loop.cc:27
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
FlutterSemanticsFlag flags
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_LOG(severity)
Definition: logging.h:82
static void DNSSD_API RegistrationCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regType, const char *domain, void *context)
NSObject< FlutterDartVMServicePublisherDelegate > * delegate
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service port
Definition: switches.h:87
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: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
#define ERROR(message)
Definition: elf_loader.cc:260
int BOOL
Definition: windows_types.h:37