Skip to content

Commit

Permalink
Advertise iOS Observatory port over mDNS/Bonjour (flutter#6533)
Browse files Browse the repository at this point in the history
* Publish port of observatory over mDNS
  • Loading branch information
dnfield authored Oct 16, 2018
1 parent 26f437f commit 62cd86c
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 2 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc
FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h
FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc
FILE: ../../../flutter/lib/ui/plugins/callback_cache.h
FILE: ../../../flutter/runtime/dart_service_isolate_unittests.cc
FILE: ../../../flutter/shell/common/isolate_configuration.cc
FILE: ../../../flutter/shell/common/isolate_configuration.h
FILE: ../../../flutter/shell/common/persistent_cache.cc
Expand All @@ -514,6 +515,8 @@ FILE: ../../../flutter/shell/platform/android/android_shell_holder.h
FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc
FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm
FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc
Expand Down
1 change: 1 addition & 0 deletions runtime/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ executable("runtime_unittests") {

sources = [
"dart_isolate_unittests.cc",
"dart_service_isolate_unittests.cc",
"dart_vm_unittests.cc",
]

Expand Down
70 changes: 68 additions & 2 deletions runtime/dart_service_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "flutter/runtime/dart_service_isolate.h"

#include <string.h>
#include <algorithm>

#include "flutter/fml/logging.h"
#include "flutter/runtime/embedder_resources.h"
Expand Down Expand Up @@ -47,19 +48,84 @@ const uint8_t* GetSymbol(Dart_NativeFunction native_function) {

} // namespace

std::mutex DartServiceIsolate::callbacks_mutex_;

FML_GUARDED_BY(DartServiceIsolate::callbacks_mutex_)
std::set<std::unique_ptr<DartServiceIsolate::ObservatoryServerStateCallback>>
DartServiceIsolate::callbacks_;

void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) {
Dart_Handle exception = nullptr;
std::string uri =
tonic::DartConverter<std::string>::FromArguments(args, 0, exception);
if (!exception) {
observatory_uri_ = uri;

if (exception) {
return;
}

observatory_uri_ = uri;

// Collect callbacks to fire in a separate collection and invoke them outside
// the lock.
std::vector<DartServiceIsolate::ObservatoryServerStateCallback>
callbacks_to_fire;
{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
for (auto& callback : callbacks_) {
callbacks_to_fire.push_back(*callback.get());
}
}

for (auto callback_to_fire : callbacks_to_fire) {
callback_to_fire(uri);
}
}

std::string DartServiceIsolate::GetObservatoryUri() {
return observatory_uri_;
}

DartServiceIsolate::CallbackHandle DartServiceIsolate::AddServerStatusCallback(
DartServiceIsolate::ObservatoryServerStateCallback callback) {
if (!callback) {
return 0;
}

auto callback_pointer =
std::make_unique<DartServiceIsolate::ObservatoryServerStateCallback>(
callback);

auto handle = reinterpret_cast<CallbackHandle>(callback_pointer.get());

{
std::lock_guard<std::mutex> lock(callbacks_mutex_);
callbacks_.insert(std::move(callback_pointer));
}

if (!observatory_uri_.empty()) {
callback(observatory_uri_);
}

return handle;
}

bool DartServiceIsolate::RemoveServerStatusCallback(
CallbackHandle callback_handle) {
std::lock_guard<std::mutex> lock(callbacks_mutex_);
auto found = std::find_if(
callbacks_.begin(), callbacks_.end(),
[callback_handle](const auto& item) {
return reinterpret_cast<CallbackHandle>(item.get()) == callback_handle;
});

if (found == callbacks_.end()) {
return false;
}

callbacks_.erase(found);
return true;
}

void DartServiceIsolate::Shutdown(Dart_NativeArguments args) {
// NO-OP.
}
Expand Down
24 changes: 24 additions & 0 deletions runtime/dart_service_isolate.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@
#ifndef FLUTTER_RUNTIME_DART_SERVICE_ISOLATE_H_
#define FLUTTER_RUNTIME_DART_SERVICE_ISOLATE_H_

#include <functional>
#include <mutex>
#include <set>
#include <string>

#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/synchronization/thread_annotations.h"

#include "third_party/dart/runtime/include/dart_api.h"

namespace blink {

class DartServiceIsolate {
public:
using ObservatoryServerStateCallback =
std::function<void(const std::string&)>;

static bool Startup(std::string server_ip,
intptr_t server_port,
Dart_LibraryTagHandler embedder_tag_handler,
Expand All @@ -21,10 +30,25 @@ class DartServiceIsolate {

static std::string GetObservatoryUri();

using CallbackHandle = ptrdiff_t;

// Returns a handle for the callback that can be used in
// RemoveServerStatusCallback
FML_WARN_UNUSED_RESULT
static CallbackHandle AddServerStatusCallback(
ObservatoryServerStateCallback callback);

// Accepts the handle returned by AddServerStatusCallback
static bool RemoveServerStatusCallback(CallbackHandle handle);

private:
// Native entries.
static void NotifyServerState(Dart_NativeArguments args);
static void Shutdown(Dart_NativeArguments args);

static std::mutex callbacks_mutex_;
static std::set<std::unique_ptr<ObservatoryServerStateCallback>> callbacks_
FML_GUARDED_BY(callbacks_mutex_);
};

} // namespace blink
Expand Down
17 changes: 17 additions & 0 deletions runtime/dart_service_isolate_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2018 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.

#include "flutter/runtime/dart_service_isolate.h"
#include "flutter/testing/testing.h"

namespace blink {

TEST(DartServiceIsolateTest, CanAddAndRemoveHandles) {
ASSERT_EQ(DartServiceIsolate::AddServerStatusCallback(nullptr), 0);
auto handle = DartServiceIsolate::AddServerStatusCallback([](const auto&) {});
ASSERT_NE(handle, 0);
ASSERT_TRUE(DartServiceIsolate::RemoveServerStatusCallback(handle));
}

} // namespace blink
2 changes: 2 additions & 0 deletions shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ shared_library("create_flutter_framework_dylib") {
"framework/Source/FlutterDartProject_Internal.h",
"framework/Source/FlutterHeadlessDartRunner.mm",
"framework/Source/FlutterNavigationController.mm",
"framework/Source/FlutterObservatoryPublisher.h",
"framework/Source/FlutterObservatoryPublisher.mm",
"framework/Source/FlutterPlatformPlugin.h",
"framework/Source/FlutterPlatformPlugin.mm",
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 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.

#ifndef FLUTTER_FLUTTEROBSERVATORYPUBLISHER_H_
#define FLUTTER_FLUTTEROBSERVATORYPUBLISHER_H_

#import <Foundation/Foundation.h>

@interface FlutterObservatoryPublisher : NSObject

@end

#endif // FLUTTER_FLUTTEROBSERVATORYPUBLISHER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2018 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.

#define FML_USED_ON_EMBEDDER

#import "FlutterObservatoryPublisher.h"

#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/memory/weak_ptr.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#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

@implementation FlutterObservatoryPublisher {
}

#else

@interface FlutterObservatoryPublisher () <NSNetServiceDelegate>
@end

@implementation FlutterObservatoryPublisher {
fml::scoped_nsobject<NSNetService> _netService;

blink::DartServiceIsolate::CallbackHandle _callbackHandle;
std::unique_ptr<fml::WeakPtrFactory<FlutterObservatoryPublisher>> _weakFactory;
}

- (instancetype)init {
self = [super init];
NSAssert(self, @"Super must not return null on init.");

_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterObservatoryPublisher>>(self);

fml::MessageLoop::EnsureInitializedForCurrentThread();

_callbackHandle = blink::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)];
}
});
});

return self;
}

- (void)dealloc {
[_netService.get() stop];
blink::DartServiceIsolate::RemoveServerStatusCallback(std::move(_callbackHandle));
[super dealloc];
}

- (void)publishServiceProtocolPort:(std::string)uri {
if (uri.empty()) {
[_netService.get() stop];
return;
}
// uri comes in as something like 'http://127.0.0.1:XXXXX/' where XXXXX is the port
// number.
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];
}

_netService.reset([[NSNetService alloc] initWithDomain:@"local."
type:@"_dartobservatory._tcp."
name:serviceName
port:[[url port] intValue]]);

[_netService.get() setDelegate:self];
[_netService.get() publish];
}

- (void)netServiceDidPublish:(NSNetService*)sender {
FML_DLOG(INFO) << "FlutterObservatoryPublisher is ready!";
}

- (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.";
}

#endif // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE && FLUTTER_RUNTIME_MODE !=
// FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE

@end
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterObservatoryPublisher.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
Expand Down Expand Up @@ -57,6 +58,8 @@ @implementation FlutterViewController {
blink::ViewportMetrics _viewportMetrics;
int64_t _nextTextureId;
BOOL _initialized;

fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher;
}

#pragma mark - Manage and override all designated initializers
Expand Down Expand Up @@ -98,6 +101,8 @@ - (void)performCommonViewControllerInitialization {

_initialized = YES;

_publisher.reset([[FlutterObservatoryPublisher alloc] init]);

_orientationPreferences = UIInterfaceOrientationMaskAll;
_statusBarStyle = UIStatusBarStyleDefault;

Expand Down

0 comments on commit 62cd86c

Please sign in to comment.