summaryrefslogtreecommitdiff
path: root/libandroidicuinit
diff options
context:
space:
mode:
authorVictor Chang <vichang@google.com>2020-10-05 16:24:12 +0100
committerVictor Chang <vichang@google.com>2020-10-19 17:16:15 +0100
commit07c22a8aa8001b6ab2dcb786878c79aa64b8cb26 (patch)
treeebc264dfb6818cbfa67ba42b46366a427335ecd5 /libandroidicuinit
parentb356f6ee56211a555f6907aa3732d7ac034c9aee (diff)
downloadicu-07c22a8aa8001b6ab2dcb786878c79aa64b8cb26.tar.gz
Android patch: libicuuc loads data files without an explicit call
Lazily load the data files in openCommonData() in udata.cpp. With this change, libandroidicuinit is only called and linked in one place in libicuuc. Pros: 1. System or app executable doesn't need to call an Android-specific functions to load .dat files before using it. The examples of system executables are the media server and sqlite shell. This together with the new ICU4C NDK API opens the use case for app to run an executable without knowing the .dat files locations 2. Single place in libicuuc.so initializes itself with a single global guard and cleanup by the common u_cleanup() call. 3. For the ART mainline module, this eliminate the need for ART module to call AIcu_register() during the zygote init. The current solution of initializing in JNI_OnLoad is not nice because 2 copies of the libandroidicuinit static library in libandroidicu and libicu_jni may violate the One definition Rule and initializes twice (very low chance because we know the places initializing it.) 4. For i18n Mainline, it eliminates the need to add Android-specific APIs, e.g. AIcu_register(), for initialization. Cons: 1. Large patch in udata.cpp may cause more work to maintain the patch in the future. Also, add a .bp file to run the sample BreakIterator program. Bug: 132621877 Bug: 160350521 Test: device boots Test: Inspect logs of initialization from zygote, media server and sqlite3 shell Test: atest CtsIcu4cTestCases Change-Id: I6ca71349a498b41ec97f8fcee056837a82e81f5a
Diffstat (limited to 'libandroidicuinit')
-rw-r--r--libandroidicuinit/Android.bp17
-rw-r--r--libandroidicuinit/IcuRegistration.cpp30
-rw-r--r--libandroidicuinit/IcuRegistration.h19
-rw-r--r--libandroidicuinit/android_icu_init.cpp51
-rw-r--r--libandroidicuinit/include/androidicuinit/android_icu_init.h (renamed from libandroidicuinit/include/androidicuinit/android_icu_reg.h)19
5 files changed, 91 insertions, 45 deletions
diff --git a/libandroidicuinit/Android.bp b/libandroidicuinit/Android.bp
index be27d8d22..47a2762cc 100644
--- a/libandroidicuinit/Android.bp
+++ b/libandroidicuinit/Android.bp
@@ -12,18 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// A library of shared initialization code for use by the direct users of
-// ICU4C only.
+// A library of shared initialization code for use by libicuuc.so only.
cc_library_static {
name: "libandroidicuinit",
- host_supported: true,
+ host_supported: true, // for ART host testing.
native_bridge_supported: true,
- srcs: ["IcuRegistration.cpp"],
+ srcs: ["*.cpp"],
shared_libs: [
"libbase",
- "libicuuc",
"liblog",
],
+ // Caution: Don't invoke the caller, e.g. openCommonData, from this library because it could
+ // cause an indefinite loop. This library interacts with libicuuc closely, but this library is
+ // stored in a separate directory to avoid a larger patch which could incur more works when
+ // updating from the upstream.
+ header_libs: ["libicuuc_headers"],
cflags: [
"-Wall",
"-Werror",
@@ -43,8 +46,6 @@ cc_library_static {
"com.android.i18n",
],
visibility: [
- "//external/icu/android_icu4j/libcore_bridge/src/native",
- "//external/icu/libandroidicu",
- "//external/icu/icu4c/source/test:__subpackages__",
+ "//external/icu/icu4c/source/common",
],
}
diff --git a/libandroidicuinit/IcuRegistration.cpp b/libandroidicuinit/IcuRegistration.cpp
index b3c96e3c7..a6a7c7087 100644
--- a/libandroidicuinit/IcuRegistration.cpp
+++ b/libandroidicuinit/IcuRegistration.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "androidicuinit/android_icu_reg.h"
#include "IcuRegistration.h"
#include <sys/mman.h>
@@ -25,7 +24,6 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <log/log.h>
-#include <unicode/uclean.h>
#include <unicode/udata.h>
#include <unicode/utypes.h>
@@ -133,14 +131,6 @@ void IcuRegistration::Deregister() {
// Init ICU, configuring it and loading the data files.
IcuRegistration::IcuRegistration() {
- UErrorCode status = U_ZERO_ERROR;
- // Tell ICU it can *only* use our memory-mapped data.
- udata_setFileAccess(UDATA_NO_FILES, &status);
- if (status != U_ZERO_ERROR) {
- ALOGE("Couldn't initialize ICU (s_setFileAccess): %s", u_errorName(status));
- abort();
- }
-
// Note: This logic below should match the logic for ICU4J in
// TimeZoneDataFiles.java in libcore/ to ensure consistent behavior between
// ICU4C and ICU4J.
@@ -187,29 +177,14 @@ IcuRegistration::IcuRegistration() {
abort();
}
ALOGD("I18n APEX ICU file found: %s", i18nModulePath.c_str());
-
- // Failures to find the ICU data tend to be somewhat obscure because ICU loads
- // its data on first use, which can be anywhere. Force initialization up front
- // so we can report a nice clear error and bail.
- u_init(&status);
- if (status != U_ZERO_ERROR) {
- ALOGE("Couldn't initialize ICU (u_init): %s", u_errorName(status));
- abort();
- }
}
// De-init ICU, unloading the data files. Do the opposite of the above function.
IcuRegistration::~IcuRegistration() {
- // Reset libicu state to before it was loaded.
- u_cleanup();
-
// Unmap ICU data files.
icu_datamap_from_i18n_module_.reset();
icu_datamap_from_tz_module_.reset();
icu_datamap_from_data_.reset();
-
- // We don't need to call udata_setFileAccess because u_cleanup takes care of
- // it.
}
bool IcuRegistration::pathExists(const std::string& path) {
@@ -262,13 +237,14 @@ std::string IcuRegistration::getI18nModulePath() {
} // namespace androidicuinit
-extern "C" {
void android_icu_register() {
androidicuinit::IcuRegistration::Register();
}
-
void android_icu_deregister() {
androidicuinit::IcuRegistration::Deregister();
}
+
+bool android_icu_is_registered() {
+ return androidicuinit::gIcuRegistration.get() != nullptr;
}
diff --git a/libandroidicuinit/IcuRegistration.h b/libandroidicuinit/IcuRegistration.h
index e0ac13df8..a8579fd0e 100644
--- a/libandroidicuinit/IcuRegistration.h
+++ b/libandroidicuinit/IcuRegistration.h
@@ -82,4 +82,23 @@ class IcuRegistration final {
} // namespace androidicuinit
+/**
+ * Initialize the ICU and load the data from .dat files from system image and
+ * various mainline modules.
+ * If ICU has already been registered, the function calls abort() and the process terminates.
+ * This function is NOT thread-safe.
+ */
+void android_icu_register();
+
+/**
+ * Unregister and unload the data. After this call, user can re-register.
+ */
+void android_icu_deregister();
+
+/**
+ * @return true if ICU has been registered.
+ */
+bool android_icu_is_registered();
+
+
#endif // ICU_REGISTRATION_H_included
diff --git a/libandroidicuinit/android_icu_init.cpp b/libandroidicuinit/android_icu_init.cpp
new file mode 100644
index 000000000..8ef51867e
--- /dev/null
+++ b/libandroidicuinit/android_icu_init.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+
+#include "androidicuinit/android_icu_init.h"
+#include "IcuRegistration.h"
+#include <log/log.h>
+
+void android_icu_init() {
+ bool runAndroidInit = false;
+
+ // We know that the environment variables are exported early in init.environ.rc on Android.
+ #ifdef __ANDROID__
+ runAndroidInit = true;
+ #else // ART host testing environment has these env variables set.
+ runAndroidInit = getenv("ANDROID_DATA") != NULL &&
+ getenv("ANDROID_TZDATA_ROOT") != NULL &&
+ getenv("ANDROID_I18N_ROOT") != NULL;
+ #endif
+
+ if (runAndroidInit) {
+ if (!android_icu_is_registered()) {
+ android_icu_register();
+ } else {
+ ALOGE("libicuuc has already been initialized but android_icu_init() is called.");
+ }
+ }
+}
+
+void android_icu_cleanup() {
+ if (android_icu_is_registered()) {
+ android_icu_deregister();
+ } else {
+ ALOGW("libicuuc is not initialized and possibly never used, "
+ "but android_icu_cleanup() is called.");
+ }
+} \ No newline at end of file
diff --git a/libandroidicuinit/include/androidicuinit/android_icu_reg.h b/libandroidicuinit/include/androidicuinit/android_icu_init.h
index 07fbe9583..4a77e0bd9 100644
--- a/libandroidicuinit/include/androidicuinit/android_icu_reg.h
+++ b/libandroidicuinit/include/androidicuinit/android_icu_init.h
@@ -16,15 +16,14 @@
#pragma once
-// Declare C wrapper of androidicuinit::IcuRegistration
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void android_icu_register();
+/**
+ * Load the ICU common data. This function should only be called once per process,
+ * and is not thread-safe.
+ */
+void android_icu_init();
-void android_icu_deregister();
-#ifdef __cplusplus
-}
-#endif
+/**
+ * Cleanup the resources loaded by android_icu_init(). This function is not thread-safe.
+ */
+void android_icu_cleanup();