未验证 提交 0a8bd9dd 编写于 作者: D Dan Field 提交者: GitHub

Fix mDNS for iOS13 (#13451)

上级 d8d5e43e
......@@ -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
......
......@@ -10,7 +10,6 @@ config("flutter_config") {
source_set("common") {
sources = [
"runtime.h",
"settings.cc",
"settings.h",
"task_runners.cc",
......
......@@ -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" ]
}
......
// 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
......@@ -6,7 +6,6 @@
#include <vector>
#include "flutter/common/runtime.h"
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/paint_utils.h"
#include "flutter/fml/logging.h"
......
......@@ -6,7 +6,6 @@
#include <sstream>
#include "flutter/common/runtime.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/trace_event.h"
......
......@@ -9,7 +9,6 @@
#include <mutex>
#include <vector>
#include "flutter/common/runtime.h"
#include "flutter/common/settings.h"
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/file.h"
......
......@@ -10,7 +10,6 @@
#include <vector>
#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"
......
......@@ -9,7 +9,6 @@
#include <future>
#include <memory>
#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"
......
......@@ -9,7 +9,6 @@
#include <sstream>
#include <string>
#include "flutter/common/runtime.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/size.h"
......
......@@ -4,17 +4,37 @@
#define FML_USED_ON_EMBEDDER
#import <TargetConditionals.h>
#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 <dns_sd.h> // nogncheck
#include <net/if.h> // nogncheck
#endif // TARGET_IPHONE_SIMULATOR
#if FLUTTER_RELEASE
#import "FlutterObservatoryPublisher.h"
@implementation FlutterObservatoryPublisher
@end
#else // FLUTTER_RELEASE
#import <TargetConditionals.h>
// 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 <dns_sd.h>
#include <net/if.h>
#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<NSURL> url;
@end
@interface FlutterObservatoryPublisher ()
- (NSData*)createTxtData:(NSURL*)url;
#else
@property(readonly) NSString* serviceName;
@property(readonly) fml::scoped_nsobject<NSObject<FlutterObservatoryPublisherDelegate>> delegate;
@interface FlutterObservatoryPublisher () <NSNetServiceDelegate>
@end
@implementation FlutterObservatoryPublisher {
fml::scoped_nsobject<NSURL> _url;
#if TARGET_IPHONE_SIMULATOR
DNSServiceRef _dnsServiceRef;
#else // TARGET_IPHONE_SIMULATOR
fml::scoped_nsobject<NSNetService> _netService;
#endif // TARGET_IPHONE_SIMULATOR
@interface ObservatoryNSNetServiceDelegate
: NSObject <FlutterObservatoryPublisherDelegate, NSNetServiceDelegate>
@end
flutter::DartServiceIsolate::CallbackHandle _callbackHandle;
std::unique_ptr<fml::WeakPtrFactory<FlutterObservatoryPublisher>> _weakFactory;
}
@interface ObservatoryDNSServiceDelegate : NSObject <FlutterObservatoryPublisherDelegate>
@end
- (NSURL*)url {
return _url.get();
@implementation ObservatoryDNSServiceDelegate {
fml::scoped_nsobject<FlutterObservatoryPublisher> _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<fml::WeakPtrFactory<FlutterObservatoryPublisher>>(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<FlutterObservatoryPublisher> _owner;
fml::scoped_nsobject<NSNetService> _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<fml::WeakPtrFactory<FlutterObservatoryPublisher>> _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<fml::WeakPtrFactory<FlutterObservatoryPublisher>>(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<NSString*, NSData*>* 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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册