From 0a8bd9dd6fbb22a44ed2cb9f74b4d57bb57aa779 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 1 Nov 2019 15:29:15 -0700 Subject: [PATCH] Fix mDNS for iOS13 (#13451) --- ci/licenses_golden/licenses_flutter | 1 - common/BUILD.gn | 1 - common/config.gni | 16 +- common/runtime.h | 15 - flow/raster_cache.cc | 1 - runtime/dart_snapshot.cc | 1 - runtime/dart_vm.cc | 1 - shell/common/shell.cc | 1 - shell/common/shell_unittests.cc | 1 - shell/common/switches.cc | 1 - .../Source/FlutterObservatoryPublisher.mm | 272 +++++++++++------- 11 files changed, 183 insertions(+), 128 deletions(-) delete mode 100644 common/runtime.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f80943a24..d9b30258d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -18,7 +18,6 @@ FILE: ../../../flutter/assets/directory_asset_bundle.h FILE: ../../../flutter/benchmarking/benchmarking.cc FILE: ../../../flutter/benchmarking/benchmarking.h FILE: ../../../flutter/common/exported_symbols.sym -FILE: ../../../flutter/common/runtime.h FILE: ../../../flutter/common/settings.cc FILE: ../../../flutter/common/settings.h FILE: ../../../flutter/common/task_runners.cc diff --git a/common/BUILD.gn b/common/BUILD.gn index 69095bbcd..b9b050735 100644 --- a/common/BUILD.gn +++ b/common/BUILD.gn @@ -10,7 +10,6 @@ config("flutter_config") { source_set("common") { sources = [ - "runtime.h", "settings.cc", "settings.h", "task_runners.cc", diff --git a/common/config.gni b/common/config.gni index cfc4b1593..1b7d18f67 100644 --- a/common/config.gni +++ b/common/config.gni @@ -43,13 +43,23 @@ feature_defines_list = [ ] if (flutter_runtime_mode == "debug") { - feature_defines_list += [ "FLUTTER_RUNTIME_MODE=1" ] + feature_defines_list += [ + "FLUTTER_RUNTIME_MODE=1", + "FLUTTER_JIT_RUNTIME=1", + ] } else if (flutter_runtime_mode == "profile") { feature_defines_list += [ "FLUTTER_RUNTIME_MODE=2" ] } else if (flutter_runtime_mode == "release") { - feature_defines_list += [ "FLUTTER_RUNTIME_MODE=3" ] + feature_defines_list += [ + "FLUTTER_RUNTIME_MODE=3", + "FLUTTER_RELEASE=1", + ] } else if (flutter_runtime_mode == "jit_release") { - feature_defines_list += [ "FLUTTER_RUNTIME_MODE=4" ] + feature_defines_list += [ + "FLUTTER_RUNTIME_MODE=4", + "FLUTTER_RELEASE=1", + "FLUTTER_JIT_RUNTIME=1", + ] } else { feature_defines_list += [ "FLUTTER_RUNTIME_MODE=0" ] } diff --git a/common/runtime.h b/common/runtime.h deleted file mode 100644 index 096ffbd86..000000000 --- a/common/runtime.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -namespace flutter { - -#define FLUTTER_JIT_RUNTIME \ - ((FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \ - (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_JIT_RELEASE)) - -#define FLUTTER_RELEASE \ - ((FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE) || \ - (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_JIT_RELEASE)) - -} // namespace flutter diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index d047b5340..a2831f83e 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -6,7 +6,6 @@ #include -#include "flutter/common/runtime.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/paint_utils.h" #include "flutter/fml/logging.h" diff --git a/runtime/dart_snapshot.cc b/runtime/dart_snapshot.cc index a5df79bb1..0a19b3984 100644 --- a/runtime/dart_snapshot.cc +++ b/runtime/dart_snapshot.cc @@ -6,7 +6,6 @@ #include -#include "flutter/common/runtime.h" #include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/fml/trace_event.h" diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc index de9d08e3b..623242bdf 100644 --- a/runtime/dart_vm.cc +++ b/runtime/dart_vm.cc @@ -9,7 +9,6 @@ #include #include -#include "flutter/common/runtime.h" #include "flutter/common/settings.h" #include "flutter/fml/compiler_specific.h" #include "flutter/fml/file.h" diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 060957a74..bbd4320d4 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -10,7 +10,6 @@ #include #include "flutter/assets/directory_asset_bundle.h" -#include "flutter/common/runtime.h" #include "flutter/fml/file.h" #include "flutter/fml/icu_util.h" #include "flutter/fml/log_settings.h" diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 840025e99..0146619cc 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -9,7 +9,6 @@ #include #include -#include "flutter/common/runtime.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/layers/transform_layer.h" #include "flutter/fml/command_line.h" diff --git a/shell/common/switches.cc b/shell/common/switches.cc index 6d31cb99f..df2e51d28 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -9,7 +9,6 @@ #include #include -#include "flutter/common/runtime.h" #include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/fml/size.h" diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm index 147ef0d11..5af43f0ca 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm @@ -4,17 +4,37 @@ #define FML_USED_ON_EMBEDDER -#import +#import "FlutterObservatoryPublisher.h" -// NSNetService works fine on physical devices, but doesn't expose the services to regular mDNS -// queries on the Simulator. We can work around this by using the lower level C API, but that's -// only available from iOS 9.3+/macOS 10.11.4+. -#if TARGET_IPHONE_SIMULATOR -#include // nogncheck -#include // nogncheck -#endif // TARGET_IPHONE_SIMULATOR +#if FLUTTER_RELEASE -#import "FlutterObservatoryPublisher.h" +@implementation FlutterObservatoryPublisher +@end + +#else // FLUTTER_RELEASE + +#import +// NSNetService works fine on physical devices before iOS 13.2. +// However, it doesn't expose the services to regular mDNS +// queries on the Simulator or on iOS 13.2+ devices. +// +// When debugging issues with this implementation, the following is helpful: +// +// 1) Running `dns-sd -Z _dartobservatory`. This is a built-in macOS tool that +// can find advertized observatories using this method. If dns-sd can't find +// it, then the observatory is not getting advertized over any network +// interface that the host machine has access to. +// 2) The Python zeroconf package. The dns-sd tool can sometimes see things +// that aren't advertizing over a network interface - for example, simulators +// using NSNetService has been observed using dns-sd, but doesn't show up in +// the Python package (which is a high quality socket based implementation). +// If that happens, this code should be tweaked such that it shows up in both +// dns-sd's output and Python zeroconf's detection. +// 3) The Dart multicast_dns package, which is what Flutter uses to find the +// port and auth code. If the advertizement shows up in dns-sd and Python +// zeroconf but not multicast_dns, then it is a bug in multicast_dns. +#include +#include #include "flutter/fml/logging.h" #include "flutter/fml/make_copyable.h" @@ -24,124 +44,129 @@ #include "flutter/fml/task_runner.h" #include "flutter/runtime/dart_service_isolate.h" -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE || \ - FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE +@protocol FlutterObservatoryPublisherDelegate +- (instancetype)initWithOwner:(FlutterObservatoryPublisher*)owner; +- (void)publishServiceProtocolPort:(NSString*)uri; +- (void)stopService; -@implementation FlutterObservatoryPublisher { -} +@property(readonly) fml::scoped_nsobject url; +@end + +@interface FlutterObservatoryPublisher () +- (NSData*)createTxtData:(NSURL*)url; -#else +@property(readonly) NSString* serviceName; +@property(readonly) fml::scoped_nsobject> delegate; -@interface FlutterObservatoryPublisher () @end -@implementation FlutterObservatoryPublisher { - fml::scoped_nsobject _url; -#if TARGET_IPHONE_SIMULATOR - DNSServiceRef _dnsServiceRef; -#else // TARGET_IPHONE_SIMULATOR - fml::scoped_nsobject _netService; -#endif // TARGET_IPHONE_SIMULATOR +@interface ObservatoryNSNetServiceDelegate + : NSObject +@end - flutter::DartServiceIsolate::CallbackHandle _callbackHandle; - std::unique_ptr> _weakFactory; -} +@interface ObservatoryDNSServiceDelegate : NSObject +@end -- (NSURL*)url { - return _url.get(); +@implementation ObservatoryDNSServiceDelegate { + fml::scoped_nsobject _owner; + DNSServiceRef _dnsServiceRef; } -- (instancetype)init { +@synthesize url; + +- (instancetype)initWithOwner:(FlutterObservatoryPublisher*)owner { self = [super init]; NSAssert(self, @"Super must not return null on init."); - - _weakFactory = std::make_unique>(self); - - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - _callbackHandle = flutter::DartServiceIsolate::AddServerStatusCallback( - [weak = _weakFactory->GetWeakPtr(), - runner = fml::MessageLoop::GetCurrent().GetTaskRunner()](const std::string& uri) { - runner->PostTask([weak, uri]() { - if (weak) { - [weak.get() publishServiceProtocolPort:std::move(uri)]; - } - }); - }); - + _owner.reset([owner retain]); return self; } - (void)stopService { -#if TARGET_IPHONE_SIMULATOR if (_dnsServiceRef) { DNSServiceRefDeallocate(_dnsServiceRef); _dnsServiceRef = NULL; } -#else // TARGET_IPHONE_SIMULATOR - [_netService.get() stop]; - [_netService.get() setDelegate:nil]; -#endif // TARGET_IPHONE_SIMULATOR } -- (void)dealloc { - [self stopService]; - - flutter::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); - [super dealloc]; -} - -- (void)publishServiceProtocolPort:(std::string)uri { - [self stopService]; - if (uri.empty()) { - return; - } +- (void)publishServiceProtocolPort:(NSString*)uri { // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port // number. - _url.reset([[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]]); - - NSString* serviceName = - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; - - // Check to see if there's an authentication code. If there is, we'll provide - // it as a txt record so flutter tools can establish a connection. - auto path = std::string{[[_url path] UTF8String]}; - if (!path.empty()) { - // Remove leading "/" - path = path.substr(1); - } - NSData* pathData = [[[NSData alloc] initWithBytes:path.c_str() length:path.length()] autorelease]; - NSDictionary* txtDict = @{ - @"authCode" : pathData, - }; - NSData* txtData = [NSNetService dataFromTXTRecordDictionary:txtDict]; + url.reset([[[NSURL alloc] initWithString:uri] retain]); -#if TARGET_IPHONE_SIMULATOR DNSServiceFlags flags = kDNSServiceFlagsDefault; +#if TARGET_IPHONE_SIMULATOR + // Simulator needs to use local loopback explicitly to work. uint32_t interfaceIndex = if_nametoindex("lo0"); +#else // TARGET_IPHONE_SIMULATOR + // Physical devices need to request all interfaces. + uint32_t interfaceIndex = 0; +#endif // TARGET_IPHONE_SIMULATOR const char* registrationType = "_dartobservatory._tcp"; const char* domain = "local."; // default domain - uint16_t port = [[_url port] intValue]; + uint16_t port = [[url port] unsignedShortValue]; - int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, [serviceName UTF8String], - registrationType, domain, NULL, htons(port), txtData.length, - txtData.bytes, registrationCallback, NULL); + NSData* txtData = [_owner createTxtData:url.get()]; + int err = + DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, + [_owner.get().serviceName UTF8String], registrationType, domain, NULL, + htons(port), txtData.length, txtData.bytes, registrationCallback, NULL); if (err != 0) { FML_LOG(ERROR) << "Failed to register observatory port with mDNS."; } else { DNSServiceSetDispatchQueue(_dnsServiceRef, dispatch_get_main_queue()); } -#else // TARGET_IPHONE_SIMULATOR +} + +static void DNSSD_API registrationCallback(DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char* name, + const char* regType, + const char* domain, + void* context) { + if (errorCode == kDNSServiceErr_NoError) { + FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!"; + } else { + FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " + "network settings and relaunch the application."; + } +} + +@end + +@implementation ObservatoryNSNetServiceDelegate { + fml::scoped_nsobject _owner; + fml::scoped_nsobject _netService; +} + +@synthesize url; + +- (instancetype)initWithOwner:(FlutterObservatoryPublisher*)owner { + self = [super init]; + NSAssert(self, @"Super must not return null on init."); + _owner.reset([owner retain]); + return self; +} + +- (void)stopService { + [_netService.get() stop]; + [_netService.get() setDelegate:nil]; +} + +- (void)publishServiceProtocolPort:(NSString*)uri { + // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port + // number. + url.reset([[[NSURL alloc] initWithString:uri] retain]); + NSNetService* netServiceTmp = [[NSNetService alloc] initWithDomain:@"local." type:@"_dartobservatory._tcp." - name:serviceName - port:[[_url port] intValue]]; - [netServiceTmp setTXTRecordData:txtData]; + name:_owner.get().serviceName + port:[[url port] intValue]]; + [netServiceTmp setTXTRecordData:[_owner createTxtData:url.get()]]; _netService.reset(netServiceTmp); [_netService.get() setDelegate:self]; [_netService.get() publish]; -#endif // TARGET_IPHONE_SIMULATOR } - (void)netServiceDidPublish:(NSNetService*)sender { @@ -153,24 +178,67 @@ "network settings and relaunch the application."; } -#if TARGET_IPHONE_SIMULATOR -static void DNSSD_API registrationCallback(DNSServiceRef sdRef, - DNSServiceFlags flags, - DNSServiceErrorType errorCode, - const char* name, - const char* regType, - const char* domain, - void* context) { - if (errorCode == kDNSServiceErr_NoError) { - FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!"; +@end + +@implementation FlutterObservatoryPublisher { + flutter::DartServiceIsolate::CallbackHandle _callbackHandle; + std::unique_ptr> _weakFactory; +} + +- (NSURL*)url { + return [_delegate.get().url autorelease]; +} + +- (instancetype)init { + self = [super init]; + NSAssert(self, @"Super must not return null on init."); + + if (@available(iOS 9.3, *)) { + _delegate.reset([[ObservatoryDNSServiceDelegate alloc] initWithOwner:self]); } else { - FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " - "network settings and relaunch the application."; + _delegate.reset([[ObservatoryNSNetServiceDelegate alloc] initWithOwner:self]); } + _weakFactory = std::make_unique>(self); + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + _callbackHandle = flutter::DartServiceIsolate::AddServerStatusCallback( + [weak = _weakFactory->GetWeakPtr(), + runner = fml::MessageLoop::GetCurrent().GetTaskRunner()](const std::string& uri) { + if (!uri.empty()) { + runner->PostTask([weak, uri]() { + if (weak) { + [[weak.get() delegate] + publishServiceProtocolPort:[NSString stringWithUTF8String:uri.c_str()]]; + } + }); + } + }); + + return self; +} + +- (NSString*)serviceName { + return NSBundle.mainBundle.bundleIdentifier; +} + +- (NSData*)createTxtData:(NSURL*)url { + // Check to see if there's an authentication code. If there is, we'll provide + // it as a txt record so flutter tools can establish a connection. + NSString* path = [[url path] substringFromIndex:MIN(1, [[url path] length])]; + NSData* pathData = [path dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary* txtDict = @{ + @"authCode" : pathData, + }; + return [NSNetService dataFromTXTRecordDictionary:txtDict]; } -#endif // TARGET_IPHONE_SIMULATOR -#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE != - // FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE +- (void)dealloc { + [_delegate stopService]; + flutter::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); + [super dealloc]; +} @end + +#endif // FLUTTER_RELEASE -- GitLab