Support ICU on Android
This CL adds an icu_data application that vends ICU data to clients via shared
memory. We use a sha1hash to validate the integrity of the data. Currently,
this icu_data service is used on Android only. Desktop platforms load the ICU
data table from the file system, as they did before. See the mojo-dev
discussion for more background.
R=jamesr@chromium.org
Review URL: https://codereview.chromium.org/826093004
diff --git a/services/icu_data/BUILD.gn b/services/icu_data/BUILD.gn
new file mode 100644
index 0000000..98b5a56
--- /dev/null
+++ b/services/icu_data/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2015 The Chromium 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("//mojo/public/mojo_application.gni")
+import("//mojo/public/tools/bindings/mojom.gni")
+
+action("embed_icu_data") {
+ script = "embed_icu_data.py"
+
+ inputs = [
+ "$root_build_dir/icudtl.dat",
+ ]
+
+ outputs = [
+ "$root_gen_dir/mojo/icu_data/data.cc",
+ ]
+
+ args = rebase_path(inputs + outputs, root_build_dir)
+
+ deps = [
+ "//third_party/icu:icudata",
+ ]
+}
+
+mojo_native_application("icu_data") {
+ sources = [
+ "icu_data_impl.cc",
+ "data.h",
+ "$root_gen_dir/mojo/icu_data/data.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ ":embed_icu_data",
+ ":interfaces",
+ ]
+}
+
+mojom("interfaces") {
+ sources = [
+ "icu_data.mojom",
+ ]
+}
diff --git a/services/icu_data/data.h b/services/icu_data/data.h
new file mode 100644
index 0000000..e7da005
--- /dev/null
+++ b/services/icu_data/data.h
@@ -0,0 +1,17 @@
+// Copyright 2015 The Chromium 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 SERVICES_ICU_DATA_DATA_H_
+#define SERVICES_ICU_DATA_DATA_H_
+
+#include <stdint.h>
+
+namespace icu_data {
+
+extern const char kICUDataTableHash[];
+extern const char kICUDataTable[];
+extern const size_t kICUDataTableSize;
+}
+
+#endif // SERVICES_ICU_DATA_DATA_H_
diff --git a/services/icu_data/embed_icu_data.py b/services/icu_data/embed_icu_data.py
new file mode 100755
index 0000000..0ffbace
--- /dev/null
+++ b/services/icu_data/embed_icu_data.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# Copyright 2015 The Chromium 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 os
+import sys
+import hashlib
+
+in_file = sys.argv[1]
+out_file = sys.argv[2]
+
+out_dir = os.path.dirname(out_file)
+
+data = None
+with open(in_file, "rb") as f:
+ data = f.read()
+
+if not os.path.exists(out_dir):
+ os.makedirs(out_dir)
+
+sha1hash = hashlib.sha1(data).hexdigest()
+
+values = ["0x%02x" % ord(c) for c in data]
+lines = []
+chunk_size = 16
+for i in range(0, len(values), chunk_size):
+ lines.append(", ".join(values[i: i + chunk_size]))
+
+with open(out_file, "w") as f:
+ f.write('#include "services/icu_data/data.h"\n')
+ f.write("namespace icu_data {\n")
+ f.write("const char kICUDataTable[] = {\n")
+ f.write(",\n".join(lines))
+ f.write("\n};\n")
+ f.write("const size_t kICUDataTableSize = sizeof(kICUDataTable);\n")
+ f.write("const char kICUDataTableHash[] = \"%s\";\n" % sha1hash)
+ f.write("}\n")
diff --git a/services/icu_data/icu_data.mojom b/services/icu_data/icu_data.mojom
new file mode 100644
index 0000000..53f8ac5
--- /dev/null
+++ b/services/icu_data/icu_data.mojom
@@ -0,0 +1,9 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module icu_data;
+
+interface ICUData {
+ Map(string sha1hash) => (handle<shared_buffer>? icu_data);
+};
diff --git a/services/icu_data/icu_data_impl.cc b/services/icu_data/icu_data_impl.cc
new file mode 100644
index 0000000..af6d99e
--- /dev/null
+++ b/services/icu_data/icu_data_impl.cc
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium 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 "mojo/application/application_runner_chromium.h"
+#include "mojo/common/weak_binding_set.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "services/icu_data/data.h"
+#include "services/icu_data/icu_data.mojom.h"
+
+namespace icu_data {
+
+class ICUDataImpl : public mojo::ApplicationDelegate,
+ public ICUData,
+ public mojo::InterfaceFactory<ICUData> {
+ public:
+ ICUDataImpl() {}
+ ~ICUDataImpl() override {}
+
+ // mojo::ApplicationDelegate implementation.
+ bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ connection->AddService(this);
+ return true;
+ }
+
+ // mojo::InterfaceFactory<mojo::ICUData> implementation.
+ void Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<ICUData> request) override {
+ bindings_.AddBinding(this, request.Pass());
+ }
+
+ void Map(const mojo::String& sha1hash,
+ const mojo::Callback<void(mojo::ScopedSharedBufferHandle)>& callback)
+ override {
+ if (std::string(sha1hash) != std::string(kICUDataTableHash)) {
+ callback.Run(mojo::ScopedSharedBufferHandle());
+ return;
+ }
+ EnsureBuffer();
+ mojo::ScopedSharedBufferHandle handle;
+ // FIXME: We should create a read-only duplicate of the handle.
+ mojo::DuplicateBuffer(buffer_->handle.get(), nullptr, &handle);
+ callback.Run(handle.Pass());
+ }
+
+ private:
+ void EnsureBuffer() {
+ if (buffer_)
+ return;
+ buffer_.reset(new mojo::SharedBuffer(kICUDataTableSize));
+ void* ptr = nullptr;
+ MojoResult rv = mojo::MapBuffer(buffer_->handle.get(), 0, kICUDataTableSize,
+ &ptr, MOJO_MAP_BUFFER_FLAG_NONE);
+ CHECK_EQ(rv, MOJO_RESULT_OK);
+ memcpy(ptr, kICUDataTable, kICUDataTableSize);
+ rv = mojo::UnmapBuffer(ptr);
+ CHECK_EQ(rv, MOJO_RESULT_OK);
+ }
+
+ scoped_ptr<mojo::SharedBuffer> buffer_;
+ mojo::WeakBindingSet<ICUData> bindings_;
+};
+}
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(new icu_data::ICUDataImpl);
+ return runner.Run(shell_handle);
+}