From 843c6a33d08b2982401e7b3709c3e60621509075 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 30 Oct 2018 00:08:16 -0700 Subject: [PATCH 1/3] Ensure Simulator publishes mDNS records --- .../Source/FlutterObservatoryPublisher.mm | 80 ++++++++++++------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm index 495a7c72ac8f3..7c93a1d5feca8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm @@ -4,6 +4,9 @@ #define FML_USED_ON_EMBEDDER +#include +#include + #import "FlutterObservatoryPublisher.h" #include "flutter/fml/logging.h" @@ -22,11 +25,9 @@ @implementation FlutterObservatoryPublisher { #else -@interface FlutterObservatoryPublisher () -@end - @implementation FlutterObservatoryPublisher { - fml::scoped_nsobject _netService; + DNSServiceRef _dnsServiceRef; + bool _dnsServiceRefInitialized; blink::DartServiceIsolate::CallbackHandle _callbackHandle; std::unique_ptr> _weakFactory; @@ -36,6 +37,7 @@ - (instancetype)init { self = [super init]; NSAssert(self, @"Super must not return null on init."); + _dnsServiceRefInitialized = false; _weakFactory = std::make_unique>(self); fml::MessageLoop::EnsureInitializedForCurrentThread(); @@ -54,14 +56,28 @@ - (instancetype)init { } - (void)dealloc { - [_netService.get() stop]; + if (_dnsServiceRefInitialized) { + DNSServiceRefDeallocate(_dnsServiceRef); + } blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); [super dealloc]; } +- (uint32_t)resolveInterface { + // We want to use the loopback interface on the simulator, to force it to be available via + // standard mDNS queries. +#if TARGET_IPHONE_SIMULATOR + return if_nametoindex("lo0"); +#else // TARGET_IPHONE_SIMULATOR + return 0; +#endif // TARGET_IPHONE_SIMULATOR +} + - (void)publishServiceProtocolPort:(std::string)uri { + if (_dnsServiceRefInitialized) { + DNSServiceRefDeallocate(_dnsServiceRef); + } if (uri.empty()) { - [_netService.get() stop]; return; } // uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port @@ -69,33 +85,41 @@ - (void)publishServiceProtocolPort:(std::string)uri { NSURL* url = [[[NSURL alloc] initWithString:[NSString stringWithUTF8String:uri.c_str()]] autorelease]; - // DNS name has to be a max of 63 bytes. Prefer to cut off the app name rather than - // the device hostName. e.g. 'io.flutter.example@someones-iphone', or - // 'ongAppNameBecauseThisCouldHappenAtSomePoint@somelongname-iphone' - NSString* serviceName = [NSString - stringWithFormat:@"%@@%@", - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"], - [NSProcessInfo processInfo].hostName]; - if ([serviceName length] > 63) { - serviceName = [serviceName substringFromIndex:[serviceName length] - 63]; - } + NSString* serviceName = + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; - _netService.reset([[NSNetService alloc] initWithDomain:@"local." - type:@"_dartobservatory._tcp." - name:serviceName - port:[[url port] intValue]]); + DNSServiceFlags flags = kDNSServiceFlagsDefault; + uint32_t interfaceIndex = [self resolveInterface]; + const char* registrationType = "_dartobservatory._tcp"; + const char* domain = "local."; // default domain + uint16_t port = [[url port] intValue]; - [_netService.get() setDelegate:self]; - [_netService.get() publish]; -} + int err = DNSServiceRegister(&_dnsServiceRef, flags, interfaceIndex, [serviceName UTF8String], + registrationType, domain, NULL, htons(port), 0, NULL, + registrationCallback, NULL); -- (void)netServiceDidPublish:(NSNetService*)sender { - FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!"; + if (err != 0) { + FML_LOG(ERROR) << "Failed to register observatory port with mDNS."; + } else { + DNSServiceProcessResult(_dnsServiceRef); + } + + _dnsServiceRefInitialized = err == 0; } -- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict { - FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " - "network settings and relaunch the application."; +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_LOG(ERROR) << "FlutterObservatoryPublisher is ready!"; + } else { + FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " + "network settings and relaunch the application."; + } } #endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE != From 81c04959c803187eef880a2916af820da8e69ff2 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 30 Oct 2018 01:13:33 -0700 Subject: [PATCH 2/3] Guard API for older versions --- .../Source/FlutterObservatoryPublisher.mm | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm index 7c93a1d5feca8..3ec723469cd1b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm @@ -4,6 +4,9 @@ #define FML_USED_ON_EMBEDDER +// 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+. #include #include @@ -25,9 +28,15 @@ @implementation FlutterObservatoryPublisher { #else +@interface FlutterObservatoryPublisher () +@end + @implementation FlutterObservatoryPublisher { +#if TARGET_IPHONE_SIMULATOR DNSServiceRef _dnsServiceRef; - bool _dnsServiceRefInitialized; +#else // TARGET_IPHONE_SIMULATOR + fml::scoped_nsobject _netService; +#endif // TARGET_IPHONE_SIMULATOR blink::DartServiceIsolate::CallbackHandle _callbackHandle; std::unique_ptr> _weakFactory; @@ -37,7 +46,6 @@ - (instancetype)init { self = [super init]; NSAssert(self, @"Super must not return null on init."); - _dnsServiceRefInitialized = false; _weakFactory = std::make_unique>(self); fml::MessageLoop::EnsureInitializedForCurrentThread(); @@ -56,27 +64,28 @@ - (instancetype)init { } - (void)dealloc { - if (_dnsServiceRefInitialized) { +#if TARGET_IPHONE_SIMULATOR + if (_dnsServiceRef) { DNSServiceRefDeallocate(_dnsServiceRef); + _dnsServiceRef = NULL; } - blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); - [super dealloc]; -} - -- (uint32_t)resolveInterface { - // We want to use the loopback interface on the simulator, to force it to be available via - // standard mDNS queries. -#if TARGET_IPHONE_SIMULATOR - return if_nametoindex("lo0"); #else // TARGET_IPHONE_SIMULATOR - return 0; + [_netService.get() stop]; #endif // TARGET_IPHONE_SIMULATOR + + blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle)); + [super dealloc]; } - (void)publishServiceProtocolPort:(std::string)uri { - if (_dnsServiceRefInitialized) { +#if TARGET_IPHONE_SIMULATOR + if (_dnsServiceRef) { DNSServiceRefDeallocate(_dnsServiceRef); + _dnsServiceRef = NULL; } +#else // TARGET_IPHONE_SIMULATOR + [_netService.get() stop]; +#endif // TARGET_IPHONE_SIMULATOR if (uri.empty()) { return; } @@ -88,8 +97,9 @@ - (void)publishServiceProtocolPort:(std::string)uri { NSString* serviceName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; +#if TARGET_IPHONE_SIMULATOR DNSServiceFlags flags = kDNSServiceFlagsDefault; - uint32_t interfaceIndex = [self resolveInterface]; + uint32_t interfaceIndex = if_nametoindex("lo0"); const char* registrationType = "_dartobservatory._tcp"; const char* domain = "local."; // default domain uint16_t port = [[url port] intValue]; @@ -103,10 +113,26 @@ - (void)publishServiceProtocolPort:(std::string)uri { } else { DNSServiceProcessResult(_dnsServiceRef); } +#else // TARGET_IPHONE_SIMULATOR + _netService.reset([[NSNetService alloc] initWithDomain:@"local." + type:@"_dartobservatory._tcp." + name:serviceName + port:[[url port] intValue]]); + [_netService.get() setDelegate:self]; + [_netService.get() publish]; +#endif // TARGET_IPHONE_SIMULATOR +} + +- (void)netServiceDidPublish:(NSNetService*)sender { + FML_LOG(INFO) << "FlutterObservatoryPublisher is ready!"; +} - _dnsServiceRefInitialized = err == 0; +- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict { + FML_LOG(ERROR) << "Could not register as server for FlutterObservatoryPublisher. Check your " + "network settings and relaunch the application."; } +#if TARGET_IPHONE_SIMULATOR static void DNSSD_API registrationCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, @@ -121,6 +147,7 @@ static void DNSSD_API registrationCallback(DNSServiceRef sdRef, "network settings and relaunch the application."; } } +#endif // TARGET_IPHONE_SIMULATOR #endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE != // FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE From 6c8b9b89d942286c18f22fb3afce253badfeaee1 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 30 Oct 2018 17:33:44 -0700 Subject: [PATCH 3/3] guard import of dns-sd api --- .../ios/framework/Source/FlutterObservatoryPublisher.mm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm index 3ec723469cd1b..73490540a61b9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm @@ -4,11 +4,15 @@ #define FML_USED_ON_EMBEDDER +#import + // 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+. -#include -#include +#if TARGET_IPHONE_SIMULATOR +#include // nogncheck +#include // nogncheck +#endif // TARGET_IPHONE_SIMLUATOR #import "FlutterObservatoryPublisher.h"