summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--luni/src/main/native/Register.cpp17
-rw-r--r--luni/src/main/native/libcore_icu_ICU.cpp152
2 files changed, 139 insertions, 30 deletions
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 5fd4a7d7dd..52df40e598 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -53,3 +53,20 @@ jint JNI_OnLoad(JavaVM* vm, void*) {
return JNI_VERSION_1_6;
}
+
+// DalvikVM calls this on shutdown, do any global cleanup here.
+// -- Very important if we restart multiple DalvikVMs in the same process to reset the state.
+void JNI_OnUnload(JavaVM* vm, void*) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("JavaVM::GetEnv() failed");
+ abort();
+ }
+ ALOGV("libjavacore JNI_OnUnload");
+
+ ScopedLocalFrame localFrame(env);
+
+#define UNREGISTER(FN) extern void FN(JNIEnv*); FN(env)
+ UNREGISTER(unregister_libcore_icu_ICU);
+#undef UNREGISTER
+}
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 3eda9233c8..63be8c3774 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -839,55 +839,136 @@ static JNINativeMethod gMethods[] = {
};
#define FAIL_WITH_STRERROR(s) \
- ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
+ ALOGE("Couldn't " s " '%s': %s", path_.c_str(), strerror(errno)); \
return FALSE;
#define MAYBE_FAIL_WITH_ICU_ERROR(s) \
if (status != U_ZERO_ERROR) {\
- ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
+ ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path_.c_str()); \
return FALSE; \
}
-static bool mapIcuData(const std::string& path) {
+// Contain the memory map for ICU data files.
+// Automatically adds the data file to ICU's list of data files upon constructing.
+//
+// - Automatically unmaps in the destructor.
+struct IcuDataMap {
+ // Map in ICU data at the path, returning null if it failed (prints error to ALOGE).
+ static std::unique_ptr<IcuDataMap> Create(const std::string& path) {
+ std::unique_ptr<IcuDataMap> map(new IcuDataMap(path));
+
+ if (!map->TryMap()) {
+ // madvise or ICU could fail but mmap still succeeds.
+ // Destructor will take care of cleaning up a partial init.
+ return nullptr;
+ }
+
+ return map;
+ }
+
+ // Unmap the ICU data.
+ ~IcuDataMap() {
+ TryUnmap();
+ }
+
+ private:
+ IcuDataMap(const std::string& path)
+ : path_(path),
+ data_(MAP_FAILED),
+ data_length_(0)
+ {}
+
+ bool TryMap() {
// Open the file and get its length.
- android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY)));
+
if (fd.get() == -1) {
FAIL_WITH_STRERROR("open");
}
+
struct stat sb;
if (fstat(fd.get(), &sb) == -1) {
FAIL_WITH_STRERROR("stat");
}
+ data_length_ = sb.st_size;
+
// Map it.
- void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
- if (data == MAP_FAILED) {
+ data_ = mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */);
+ if (data_ == MAP_FAILED) {
FAIL_WITH_STRERROR("mmap");
}
// Tell the kernel that accesses are likely to be random rather than sequential.
- if (madvise(data, sb.st_size, MADV_RANDOM) == -1) {
+ if (madvise(data_, data_length_, MADV_RANDOM) == -1) {
FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
}
UErrorCode status = U_ZERO_ERROR;
// Tell ICU to use our memory-mapped data.
- udata_setCommonData(data, &status);
+ udata_setCommonData(data_, &status);
MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
- return TRUE;
-}
+ return true;
+ }
-void register_libcore_icu_ICU(JNIEnv* env) {
- // Check the timezone override file exists. If it does, map it first so we use it in preference
- // to the one that shipped with the device.
+ bool TryUnmap() {
+ // Don't need to do opposite of udata_setCommonData,
+ // u_cleanup (performed in unregister_libcore_icu_ICU) takes care of it.
+
+ // Don't need to opposite of madvise, munmap will take care of it.
+
+ if (data_ != MAP_FAILED) {
+ if (munmap(data_, data_length_) == -1) {
+ FAIL_WITH_STRERROR("munmap");
+ }
+ }
+
+ // Don't need to close the file, it was closed automatically during TryMap.
+ return true;
+ }
+
+ std::string path_; // Save for error messages.
+ void* data_; // Save for munmap.
+ size_t data_length_; // Save for munmap.
+};
+
+static std::unique_ptr<IcuDataMap> sIcuDataMapFromData;
+static std::unique_ptr<IcuDataMap> sIcuDataMapFromSystem;
+
+// Check the timezone override file exists. If it does, map it first so we use it in preference
+// to the one that shipped with the device.
+static std::string getTzDataOverridePath() {
const char* dataPathPrefix = getenv("ANDROID_DATA");
if (dataPathPrefix == NULL) {
ALOGE("ANDROID_DATA environment variable not set"); \
abort();
}
+ std::string dataPath;
+ dataPath = dataPathPrefix;
+ dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+
+ return dataPath;
+}
+static std::string getSystemPath() {
+ const char* systemPathPrefix = getenv("ANDROID_ROOT");
+ if (systemPathPrefix == NULL) {
+ ALOGE("ANDROID_ROOT environment variable not set"); \
+ abort();
+ }
+
+ std::string systemPath;
+ systemPath = systemPathPrefix;
+ systemPath += "/usr/icu/";
+ systemPath += U_ICUDATA_NAME;
+ systemPath += ".dat";
+ return systemPath;
+}
+
+// Init ICU, configuring it and loading the data files.
+void register_libcore_icu_ICU(JNIEnv* env) {
UErrorCode status = U_ZERO_ERROR;
// Tell ICU it can *only* use our memory-mapped data.
udata_setFileAccess(UDATA_NO_FILES, &status);
@@ -896,15 +977,13 @@ void register_libcore_icu_ICU(JNIEnv* env) {
abort();
}
- // Map in optional TZ data files.
- std::string dataPath;
- dataPath = dataPathPrefix;
- dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+ std::string dataPath = getTzDataOverridePath();
+ // Map in optional TZ data files.
struct stat sb;
if (stat(dataPath.c_str(), &sb) == 0) {
ALOGD("Timezone override file found: %s", dataPath.c_str());
- if (!mapIcuData(dataPath)) {
+ if ((sIcuDataMapFromData = IcuDataMap::Create(dataPath)) == nullptr) {
ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
}
} else {
@@ -912,18 +991,7 @@ void register_libcore_icu_ICU(JNIEnv* env) {
}
// Use the ICU data files that shipped with the device for everything else.
- const char* systemPathPrefix = getenv("ANDROID_ROOT");
- if (systemPathPrefix == NULL) {
- ALOGE("ANDROID_ROOT environment variable not set"); \
- abort();
- }
- std::string systemPath;
- systemPath = systemPathPrefix;
- systemPath += "/usr/icu/";
- systemPath += U_ICUDATA_NAME;
- systemPath += ".dat";
-
- if (!mapIcuData(systemPath)) {
+ if ((sIcuDataMapFromSystem = IcuDataMap::Create(getSystemPath())) == nullptr) {
abort();
}
@@ -938,3 +1006,27 @@ void register_libcore_icu_ICU(JNIEnv* env) {
jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
}
+
+// De-init ICU, unloading the data files. Do the opposite of the above function.
+void unregister_libcore_icu_ICU(JNIEnv* env) {
+ // Unregister JNI native methods explicitly.
+ {
+ ScopedLocalRef<jclass> c(env, env->FindClass("libcore/icu/ICU"));
+ if (c.get() != nullptr) {
+ env->UnregisterNatives(c.get());
+ } else {
+ ALOGW("Couldn't find class libcore/icu/ICU to unregister natives");
+ }
+ }
+
+ // Reset libicu state to before it was loaded.
+ u_cleanup();
+
+ // Unmap ICU data files that shipped with the device for everything else.
+ sIcuDataMapFromSystem.reset();
+
+ // Unmap optional TZ data files.
+ sIcuDataMapFromData.reset();
+
+ // We don't need to call udata_setFileAccess because u_cleanup takes care of it.
+}