flutter_flutter/fml/icu_util.cc
Jason Simmons 050dcaad60
Embed ICU data inside libflutter.so on Android (#7588)
Prior to this the Android embedder code would extract the icudtl.dat asset out
of the APK and write it to local disk during the first startup of the app.

This change will make that work unnecessary and eliminate the risk of ICU
failures due to errors in the extraction process.
2019-01-30 11:56:17 -08:00

121 lines
3.2 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 "flutter/fml/icu_util.h"
#include <memory>
#include <mutex>
#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "third_party/icu/source/common/unicode/udata.h"
namespace fml {
namespace icu {
class ICUContext {
public:
ICUContext(const std::string& icu_data_path) : valid_(false) {
valid_ = SetupMapping(icu_data_path) && SetupICU();
}
ICUContext(std::unique_ptr<Mapping> mapping) : mapping_(std::move(mapping)) {
valid_ = SetupICU();
}
~ICUContext() = default;
bool SetupMapping(const std::string& icu_data_path) {
// Check if the path exists and it readable directly.
auto fd =
fml::OpenFile(icu_data_path.c_str(), false, fml::FilePermission::kRead);
// Check the path relative to the current executable.
if (!fd.is_valid()) {
auto directory = fml::paths::GetExecutableDirectoryPath();
if (!directory.first) {
return false;
}
std::string path_relative_to_executable =
paths::JoinPaths({directory.second, icu_data_path});
fd = fml::OpenFile(path_relative_to_executable.c_str(), false,
fml::FilePermission::kRead);
}
if (!fd.is_valid()) {
return false;
}
std::initializer_list<FileMapping::Protection> protection = {
fml::FileMapping::Protection::kRead};
auto file_mapping =
std::make_unique<FileMapping>(fd, std::move(protection));
if (file_mapping->GetSize() != 0) {
mapping_ = std::move(file_mapping);
return true;
}
return false;
}
bool SetupICU() {
if (GetSize() == 0) {
return false;
}
UErrorCode err_code = U_ZERO_ERROR;
udata_setCommonData(GetMapping(), &err_code);
return (err_code == U_ZERO_ERROR);
}
const uint8_t* GetMapping() const {
return mapping_ ? mapping_->GetMapping() : nullptr;
}
size_t GetSize() const { return mapping_ ? mapping_->GetSize() : 0; }
bool IsValid() const { return valid_; }
private:
bool valid_;
std::unique_ptr<Mapping> mapping_;
FML_DISALLOW_COPY_AND_ASSIGN(ICUContext);
};
void InitializeICUOnce(const std::string& icu_data_path) {
static ICUContext* context = new ICUContext(icu_data_path);
FML_CHECK(context->IsValid())
<< "Must be able to initialize the ICU context. Tried: " << icu_data_path;
}
std::once_flag g_icu_init_flag;
void InitializeICU(const std::string& icu_data_path) {
std::call_once(g_icu_init_flag,
[&icu_data_path]() { InitializeICUOnce(icu_data_path); });
}
void InitializeICUFromMappingOnce(std::unique_ptr<Mapping> mapping) {
static ICUContext* context = new ICUContext(std::move(mapping));
FML_CHECK(context->IsValid())
<< "Unable to initialize the ICU context from a mapping.";
}
void InitializeICUFromMapping(std::unique_ptr<Mapping> mapping) {
std::call_once(g_icu_init_flag, [mapping = std::move(mapping)]() mutable {
InitializeICUFromMappingOnce(std::move(mapping));
});
}
} // namespace icu
} // namespace fml