mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
The timezone data in Fuchsia is at a fixed path. This will have the flutter runner attempt to load it and log, but not fail if loading does not work out. Added two tests (1) Shows that the specific TZ data version loaded takes effect after initialization (2) Shows that when TZ data files are absent the initialization continues.
314 lines
10 KiB
C++
314 lines
10 KiB
C++
// 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.
|
|
|
|
#include "runner.h"
|
|
|
|
#include <fuchsia/mem/cpp/fidl.h>
|
|
#include <lib/async/cpp/task.h>
|
|
#include <lib/trace-engine/instrumentation.h>
|
|
#include <zircon/status.h>
|
|
#include <zircon/types.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include "flutter/fml/make_copyable.h"
|
|
#include "flutter/lib/ui/text/font_collection.h"
|
|
#include "flutter/runtime/dart_vm.h"
|
|
#include "lib/sys/cpp/component_context.h"
|
|
#include "runtime/dart/utils/files.h"
|
|
#include "runtime/dart/utils/vmo.h"
|
|
#include "runtime/dart/utils/vmservice_object.h"
|
|
#include "third_party/icu/source/common/unicode/udata.h"
|
|
#include "third_party/skia/include/core/SkGraphics.h"
|
|
|
|
namespace flutter_runner {
|
|
|
|
namespace {
|
|
|
|
static constexpr char kIcuDataPath[] = "/pkg/data/icudtl.dat";
|
|
|
|
// Environment variable containing the path to the directory containing the
|
|
// timezone files.
|
|
static constexpr char kICUTZEnv[] = "ICU_TIMEZONE_FILES_DIR";
|
|
|
|
// The data directory containing ICU timezone data files.
|
|
static constexpr char kICUTZDataDir[] = "/config/data/tzdata/icu/44/le";
|
|
|
|
// Map the memory into the process and return a pointer to the memory.
|
|
uintptr_t GetICUData(const fuchsia::mem::Buffer& icu_data) {
|
|
uint64_t data_size = icu_data.size;
|
|
if (data_size > std::numeric_limits<size_t>::max())
|
|
return 0u;
|
|
|
|
uintptr_t data = 0u;
|
|
zx_status_t status = zx::vmar::root_self()->map(
|
|
0, icu_data.vmo, 0, static_cast<size_t>(data_size), ZX_VM_PERM_READ,
|
|
&data);
|
|
if (status == ZX_OK) {
|
|
return data;
|
|
}
|
|
|
|
return 0u;
|
|
}
|
|
|
|
// Initializes the timezone data if available. Timezone data file in Fuchsia
|
|
// is at a fixed directory path. Returns true on success. As a side effect
|
|
// sets the value of the environment variable "ICU_TIMEZONE_FILES_DIR" to a
|
|
// fixed value which is fuchsia-specific.
|
|
bool InitializeTZData() {
|
|
// We need the ability to change the env variable for testing, so not
|
|
// overwriting if set.
|
|
setenv(kICUTZEnv, kICUTZDataDir, 0 /* No overwrite */);
|
|
|
|
const std::string tzdata_dir = getenv(kICUTZEnv);
|
|
// Try opening the path to check if present. No need to verify that it is a
|
|
// directory since ICU loading will return an error if the TZ data path is
|
|
// wrong.
|
|
int fd = openat(AT_FDCWD, tzdata_dir.c_str(), O_RDONLY);
|
|
if (fd < 0) {
|
|
FML_LOG(INFO) << "Could not open: '" << tzdata_dir
|
|
<< "', proceeding without loading the timezone database: "
|
|
<< strerror(errno);
|
|
return false;
|
|
}
|
|
if (!close(fd)) {
|
|
FML_LOG(WARNING) << "Could not close: " << tzdata_dir << ": "
|
|
<< strerror(errno);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Return value indicates if initialization was successful.
|
|
bool InitializeICU() {
|
|
const char* data_path = kIcuDataPath;
|
|
|
|
fuchsia::mem::Buffer icu_data;
|
|
if (!dart_utils::VmoFromFilename(data_path, &icu_data)) {
|
|
return false;
|
|
}
|
|
|
|
uintptr_t data = GetICUData(icu_data);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
|
|
// If the loading fails, soldier on. The loading is optional as we don't
|
|
// want to crash the engine in transition.
|
|
InitializeTZData();
|
|
|
|
// Pass the data to ICU.
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
udata_setCommonData(reinterpret_cast<const char*>(data), &err);
|
|
if (err != U_ZERO_ERROR) {
|
|
FML_LOG(ERROR) << "error loading ICU data: " << err;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
static void SetProcessName() {
|
|
std::stringstream stream;
|
|
#if defined(DART_PRODUCT)
|
|
stream << "io.flutter.product_runner.";
|
|
#else
|
|
stream << "io.flutter.runner.";
|
|
#endif
|
|
if (flutter::DartVM::IsRunningPrecompiledCode()) {
|
|
stream << "aot";
|
|
} else {
|
|
stream << "jit";
|
|
}
|
|
const auto name = stream.str();
|
|
zx::process::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size());
|
|
}
|
|
|
|
static void SetThreadName(const std::string& thread_name) {
|
|
zx::thread::self()->set_property(ZX_PROP_NAME, thread_name.c_str(),
|
|
thread_name.size());
|
|
}
|
|
|
|
#if !defined(DART_PRODUCT)
|
|
// Register native symbol information for the Dart VM's profiler.
|
|
static void RegisterProfilerSymbols(const char* symbols_path,
|
|
const char* dso_name) {
|
|
std::string* symbols = new std::string();
|
|
if (dart_utils::ReadFileToString(symbols_path, symbols)) {
|
|
Dart_AddSymbols(dso_name, symbols->data(), symbols->size());
|
|
} else {
|
|
FML_LOG(ERROR) << "Failed to load " << symbols_path;
|
|
}
|
|
}
|
|
#endif // !defined(DART_PRODUCT)
|
|
|
|
Runner::Runner(async::Loop* loop)
|
|
: loop_(loop), context_(sys::ComponentContext::Create()) {
|
|
#if !defined(DART_PRODUCT)
|
|
// The VM service isolate uses the process-wide namespace. It writes the
|
|
// vm service protocol port under /tmp. The VMServiceObject exposes that
|
|
// port number to The Hub.
|
|
context_->outgoing()->debug_dir()->AddEntry(
|
|
dart_utils::VMServiceObject::kPortDirName,
|
|
std::make_unique<dart_utils::VMServiceObject>());
|
|
|
|
SetupTraceObserver();
|
|
#endif // !defined(DART_PRODUCT)
|
|
|
|
SkGraphics::Init();
|
|
|
|
SetupICU();
|
|
|
|
SetProcessName();
|
|
|
|
SetThreadName("io.flutter.runner.main");
|
|
|
|
context_->outgoing()->AddPublicService<fuchsia::sys::Runner>(
|
|
std::bind(&Runner::RegisterApplication, this, std::placeholders::_1));
|
|
|
|
#if !defined(DART_PRODUCT)
|
|
if (Dart_IsPrecompiledRuntime()) {
|
|
RegisterProfilerSymbols("pkg/data/flutter_aot_runner.dartprofilersymbols",
|
|
"");
|
|
} else {
|
|
RegisterProfilerSymbols("pkg/data/flutter_jit_runner.dartprofilersymbols",
|
|
"");
|
|
}
|
|
#endif // !defined(DART_PRODUCT)
|
|
}
|
|
|
|
Runner::~Runner() {
|
|
context_->outgoing()->RemovePublicService<fuchsia::sys::Runner>();
|
|
|
|
#if !defined(DART_PRODUCT)
|
|
trace_observer_->Stop();
|
|
#endif // !defined(DART_PRODUCT)
|
|
}
|
|
|
|
void Runner::RegisterApplication(
|
|
fidl::InterfaceRequest<fuchsia::sys::Runner> request) {
|
|
active_applications_bindings_.AddBinding(this, std::move(request));
|
|
}
|
|
|
|
void Runner::StartComponent(
|
|
fuchsia::sys::Package package,
|
|
fuchsia::sys::StartupInfo startup_info,
|
|
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
|
|
// TRACE_DURATION currently requires that the string data does not change
|
|
// in the traced scope. Since |package| gets moved in the Application::Create
|
|
// call below, we cannot ensure that |package.resolved_url| does not move or
|
|
// change, so we make a copy to pass to TRACE_DURATION.
|
|
// TODO(PT-169): Remove this copy when TRACE_DURATION reads string arguments
|
|
// eagerly.
|
|
std::string url_copy = package.resolved_url;
|
|
TRACE_EVENT1("flutter", "StartComponent", "url", url_copy.c_str());
|
|
// Notes on application termination: Application typically terminate on the
|
|
// thread on which they were created. This usually means the thread was
|
|
// specifically created to host the application. But we want to ensure that
|
|
// access to the active applications collection is made on the same thread. So
|
|
// we capture the runner in the termination callback. There is no risk of
|
|
// there being multiple application runner instance in the process at the same
|
|
// time. So it is safe to use the raw pointer.
|
|
Application::TerminationCallback termination_callback =
|
|
[task_runner = loop_->dispatcher(), //
|
|
application_runner = this //
|
|
](const Application* application) {
|
|
async::PostTask(task_runner, [application_runner, application]() {
|
|
application_runner->OnApplicationTerminate(application);
|
|
});
|
|
};
|
|
|
|
auto active_application = Application::Create(
|
|
std::move(termination_callback), // termination callback
|
|
std::move(package), // application package
|
|
std::move(startup_info), // startup info
|
|
context_->svc(), // runner incoming services
|
|
std::move(controller) // controller request
|
|
);
|
|
|
|
auto key = active_application.application.get();
|
|
active_applications_[key] = std::move(active_application);
|
|
}
|
|
|
|
void Runner::OnApplicationTerminate(const Application* application) {
|
|
auto app = active_applications_.find(application);
|
|
if (app == active_applications_.end()) {
|
|
FML_LOG(INFO)
|
|
<< "The remote end of the application runner tried to terminate an "
|
|
"application that has already been terminated, possibly because we "
|
|
"initiated the termination";
|
|
return;
|
|
}
|
|
auto& active_application = app->second;
|
|
|
|
// Grab the items out of the entry because we will have to rethread the
|
|
// destruction.
|
|
auto application_to_destroy = std::move(active_application.application);
|
|
auto application_thread = std::move(active_application.thread);
|
|
|
|
// Delegate the entry.
|
|
active_applications_.erase(application);
|
|
|
|
// Post the task to destroy the application and quit its message loop.
|
|
async::PostTask(
|
|
application_thread->dispatcher(),
|
|
fml::MakeCopyable([instance = std::move(application_to_destroy),
|
|
thread = application_thread.get()]() mutable {
|
|
instance.reset();
|
|
thread->Quit();
|
|
}));
|
|
|
|
// This works because just posted the quit task on the hosted thread.
|
|
application_thread->Join();
|
|
}
|
|
|
|
void Runner::SetupICU() {
|
|
// Exposes the TZ data setup for testing. Failing here is not fatal.
|
|
Runner::SetupTZDataInternal();
|
|
if (!Runner::SetupICUInternal()) {
|
|
FML_LOG(ERROR) << "Could not initialize ICU data.";
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool Runner::SetupICUInternal() {
|
|
return InitializeICU();
|
|
}
|
|
|
|
// static
|
|
bool Runner::SetupTZDataInternal() {
|
|
return InitializeTZData();
|
|
}
|
|
|
|
#if !defined(DART_PRODUCT)
|
|
void Runner::SetupTraceObserver() {
|
|
trace_observer_ = std::make_unique<trace::TraceObserver>();
|
|
trace_observer_->Start(loop_->dispatcher(), [runner = this]() {
|
|
if (!trace_is_category_enabled("dart:profiler")) {
|
|
return;
|
|
}
|
|
if (trace_state() == TRACE_STARTED) {
|
|
runner->prolonged_context_ = trace_acquire_prolonged_context();
|
|
Dart_StartProfiling();
|
|
} else if (trace_state() == TRACE_STOPPING) {
|
|
for (auto& it : runner->active_applications_) {
|
|
fml::AutoResetWaitableEvent latch;
|
|
async::PostTask(it.second.thread->dispatcher(), [&]() {
|
|
it.second.application->WriteProfileToTrace();
|
|
latch.Signal();
|
|
});
|
|
latch.Wait();
|
|
}
|
|
Dart_StopProfiling();
|
|
trace_release_prolonged_context(runner->prolonged_context_);
|
|
}
|
|
});
|
|
}
|
|
#endif // !defined(DART_PRODUCT)
|
|
|
|
} // namespace flutter_runner
|