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.
This commit is contained in:
Jason Simmons 2019-01-30 11:56:17 -08:00 committed by GitHub
parent c92df428ef
commit 050dcaad60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 130 additions and 22 deletions

2
DEPS
View File

@ -116,7 +116,7 @@ allowed_hosts = [
]
deps = {
'src': 'https://github.com/flutter/buildroot.git' + '@' + '19317704cfb7be72c7d81953f634674ea8bee5ea',
'src': 'https://github.com/flutter/buildroot.git' + '@' + '13ca742ec8b3d7761877197d74b003d3e646d805',
# Fuchsia compatibility
#

View File

@ -391,6 +391,7 @@ FILE: ../../../flutter/shell/platform/android/android_context_gl.cc
FILE: ../../../flutter/shell/platform/android/android_context_gl.h
FILE: ../../../flutter/shell/platform/android/android_environment_gl.cc
FILE: ../../../flutter/shell/platform/android/android_environment_gl.h
FILE: ../../../flutter/shell/platform/android/android_exports.lst
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.cc
FILE: ../../../flutter/shell/platform/android/android_external_texture_gl.h
FILE: ../../../flutter/shell/platform/android/android_native_window.cc

View File

@ -105,6 +105,7 @@ struct Settings {
bool verbose_logging = false;
std::string log_tag = "flutter";
std::string icu_data_path;
MappingCallback icu_mapper;
// Assets settings
fml::UniqueFD::element_type assets_dir =

View File

@ -10,6 +10,7 @@
#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"
@ -22,6 +23,10 @@ class ICUContext {
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) {
@ -99,5 +104,17 @@ void InitializeICU(const std::string& icu_data_path) {
[&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

View File

@ -8,12 +8,15 @@
#include <string>
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
namespace fml {
namespace icu {
void InitializeICU(const std::string& icu_data_path = "");
void InitializeICUFromMapping(std::unique_ptr<Mapping> mapping);
} // namespace icu
} // namespace fml

View File

@ -189,6 +189,8 @@ static void PerformInitializationTasks(const blink::Settings& settings) {
if (settings.icu_data_path.size() != 0) {
fml::icu::InitializeICU(settings.icu_data_path);
} else if (settings.icu_mapper) {
fml::icu::InitializeICUFromMapping(settings.icu_mapper());
} else {
FML_DLOG(WARNING) << "Skipping ICU initialization in the shell.";
}

View File

@ -9,6 +9,7 @@
#include <sstream>
#include <string>
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/string_view.h"
#include "flutter/shell/version/version.h"
@ -121,6 +122,17 @@ static bool GetSwitchValue(const fml::CommandLine& command_line,
return false;
}
std::unique_ptr<fml::Mapping> GetSymbolMapping(std::string symbol_prefix) {
fml::RefPtr<fml::NativeLibrary> proc_library =
fml::NativeLibrary::CreateForCurrentProcess();
const uint8_t* mapping =
proc_library->ResolveSymbol((symbol_prefix + "_start").c_str());
const intptr_t size = reinterpret_cast<intptr_t>(
proc_library->ResolveSymbol((symbol_prefix + "_size").c_str()));
FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
}
blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
blink::Settings settings = {};
@ -213,6 +225,14 @@ blink::Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
&settings.icu_data_path);
if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
std::string icu_symbol_prefix;
command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
&icu_symbol_prefix);
settings.icu_mapper = [icu_symbol_prefix] {
return GetSymbolMapping(icu_symbol_prefix);
};
}
settings.use_test_fonts =
command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));

View File

@ -48,6 +48,10 @@ DEF_SWITCH(AotIsolateSnapshotInstructions,
"read and executable. AotSnapshotPath must be present.")
DEF_SWITCH(CacheDirPath, "cache-dir-path", "Path to the cache directory.")
DEF_SWITCH(ICUDataFilePath, "icu-data-file-path", "Path to the ICU data file.")
DEF_SWITCH(ICUSymbolPrefix,
"icu-symbol-prefix",
"Prefix for the symbols representing ICU data linked into the "
"Flutter library.")
DEF_SWITCH(DartFlags,
"dart-flags",
"Flags passed directly to the Dart VM without being interpreted "

View File

@ -49,10 +49,12 @@ shared_library("flutter_shell_native") {
"platform_view_android_jni.h",
"vsync_waiter_android.cc",
"vsync_waiter_android.h",
"$root_build_dir/flutter_icu/icudtl.o",
]
deps = [
":android_gpu_configuration",
":icudtl_object",
"$flutter_root/assets",
"$flutter_root/common",
"$flutter_root/flow",
@ -90,6 +92,8 @@ shared_library("flutter_shell_native") {
"EGL",
"GLESv2",
]
ldflags = ["-Wl,--version-script=" + rebase_path("android_exports.lst")]
}
java_library("flutter_shell_java") {
@ -200,19 +204,25 @@ java_prebuilt("android_arch_lifecycle_viewmodel") {
jar_path = "//third_party/android_support/android_arch_lifecycle_viewmodel.jar"
}
copy("flutter_shell_assets") {
visibility = [ ":*" ]
action("icudtl_object") {
script = "$flutter_root/sky/tools/objcopy.py"
sources = [
"//third_party/icu/flutter/icudtl.dat",
icudtl_input = "//third_party/icu/flutter/icudtl.dat"
icudtl_output = "$root_build_dir/flutter_icu/icudtl.o"
inputs = [
"$icudtl_input",
]
outputs = [
"$root_build_dir/flutter_shell_assets/{{source_file_part}}",
"$icudtl_output",
]
deps = [
"//third_party/icu:icudata",
args = [
"--objcopy", rebase_path(android_objcopy),
"--input", rebase_path(icudtl_input),
"--output", rebase_path(icudtl_output),
"--arch", current_cpu,
]
}
@ -222,7 +232,6 @@ action("android") {
inputs = [
"$root_build_dir/flutter_java.jar",
"$root_build_dir/lib.stripped/libflutter.so",
"$root_build_dir/flutter_shell_assets/icudtl.dat",
]
outputs = [
@ -234,8 +243,6 @@ action("android") {
rebase_path("flutter.jar", root_build_dir, root_build_dir),
"--dist_jar",
rebase_path("flutter_java.jar", root_build_dir, root_build_dir),
"--asset_dir",
rebase_path("flutter_shell_assets", root_build_dir, root_build_dir),
"--native_lib",
rebase_path("lib.stripped/libflutter.so", root_build_dir, root_build_dir),
"--android_abi",
@ -243,7 +250,6 @@ action("android") {
]
deps = [
":flutter_shell_assets",
":flutter_shell_java",
":flutter_shell_native",
]

View File

@ -0,0 +1,14 @@
# 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.
# Linker script that exports the minimal symbols required for libflutter.so
{
global:
JNI_OnLoad;
_binary_icudtl_dat_start;
_binary_icudtl_dat_size;
local:
*;
};

View File

@ -62,10 +62,6 @@ public class FlutterMain {
private static final String DEFAULT_KERNEL_BLOB = "kernel_blob.bin";
private static final String DEFAULT_FLUTTER_ASSETS_DIR = "flutter_assets";
// Assets that are shared among all Flutter apps within an APK.
private static final String SHARED_ASSET_DIR = "flutter_shared";
private static final String SHARED_ASSET_ICU_DATA = "icudtl.dat";
private static String fromFlutterAssets(String filePath) {
return sFlutterAssetsDir + File.separator + filePath;
}
@ -85,7 +81,6 @@ public class FlutterMain {
private static boolean sIsPrecompiledAsBlobs;
private static boolean sIsPrecompiledAsSharedLibrary;
private static Settings sSettings;
private static String sIcuDataPath;
private static final class ImmutableSetBuilder<T> {
static <T> ImmutableSetBuilder<T> newInstance() {
@ -188,7 +183,7 @@ public class FlutterMain {
sResourceExtractor.waitForCompletion();
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-data-file-path=" + sIcuDataPath);
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
if (args != null) {
Collections.addAll(shellArgs, args);
}
@ -284,10 +279,6 @@ public class FlutterMain {
sResourceExtractor = new ResourceExtractor(context);
String icuAssetPath = SHARED_ASSET_DIR + File.separator + SHARED_ASSET_ICU_DATA;
sResourceExtractor.addResource(icuAssetPath);
sIcuDataPath = PathUtils.getDataDirectory(applicationContext) + File.separator + icuAssetPath;
sResourceExtractor
.addResource(fromFlutterAssets(sFlx))
.addResource(fromFlutterAssets(sAotVmSnapshotData))

49
sky/tools/objcopy.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python
# 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.
import argparse
import os
import subprocess
import sys
# BFD architecture names recognized by objcopy.
BFD_ARCH = {
'arm': 'arm',
'arm64': 'aarch64',
'x86': 'i386',
'x64': 'i386:x86-64',
}
# BFD target names recognized by objcopy.
BFD_TARGET = {
'arm': 'elf32-littlearm',
'arm64': 'elf64-littleaarch64',
'x86': 'elf32-i386',
'x64': 'elf64-x86-64',
}
def main():
parser = argparse.ArgumentParser(description='Convert a data file to an object file')
parser.add_argument('--objcopy', type=str, required=True)
parser.add_argument('--input', type=str, required=True)
parser.add_argument('--output', type=str, required=True)
parser.add_argument('--arch', type=str, required=True)
args = parser.parse_args()
input_dir, input_file = os.path.split(args.input)
output_path = os.path.abspath(args.output)
subprocess.check_call([
args.objcopy,
'-I', 'binary',
'-O', BFD_TARGET[args.arch],
'-B', BFD_ARCH[args.arch],
input_file,
output_path,
], cwd=input_dir)
if __name__ == '__main__':
sys.exit(main())