Merge "CipherTest: Remove unused AES_MODES"
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 5fd4a7d..52df40e 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -53,3 +53,20 @@
 
     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 3eda923..e6f378e 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -838,56 +838,110 @@
     NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
 };
 
+//
+// Global initialization & Teardown for ICU Setup
+//   - Contains handlers for JNI_OnLoad and JNI_OnUnload
+//
+
 #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.
-    const char* dataPathPrefix = getenv("ANDROID_DATA");
-    if (dataPathPrefix == NULL) {
-        ALOGE("ANDROID_DATA environment variable not set"); \
-        abort();
+  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.
+};
+
+struct ICURegistration {
+  // Init ICU, configuring it and loading the data files.
+  ICURegistration(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 +950,13 @@
         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 ((icu_datamap_from_data_ = IcuDataMap::Create(dataPath)) == nullptr) {
             ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
         }
     } else {
@@ -912,18 +964,7 @@
     }
 
     // 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 ((icu_datamap_from_system_ = IcuDataMap::Create(getSystemPath())) == nullptr) {
         abort();
     }
 
@@ -937,4 +978,69 @@
     }
 
     jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
+  }
+
+  // De-init ICU, unloading the data files. Do the opposite of the above function.
+  ~ICURegistration() {
+    // Skip unregistering JNI methods explicitly, class unloading takes care of it.
+
+    // Reset libicu state to before it was loaded.
+    u_cleanup();
+
+    // Unmap ICU data files that shipped with the device for everything else.
+    icu_datamap_from_system_.reset();
+
+    // Unmap optional TZ data files.
+    icu_datamap_from_data_.reset();
+
+    // We don't need to call udata_setFileAccess because u_cleanup takes care of it.
+  }
+
+  // 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;
+  }
+
+  std::unique_ptr<IcuDataMap> icu_datamap_from_data_;
+  std::unique_ptr<IcuDataMap> icu_datamap_from_system_;
+};
+
+// Use RAII-style initialization/teardown so that we can get unregistered
+// when dlclose is called (even if JNI_OnUnload is not).
+static std::unique_ptr<ICURegistration> sIcuRegistration;
+
+// Init ICU, configuring it and loading the data files.
+void register_libcore_icu_ICU(JNIEnv* env) {
+  sIcuRegistration.reset(new ICURegistration(env));
+}
+
+// De-init ICU, unloading the data files. Do the opposite of the above function.
+void unregister_libcore_icu_ICU(JNIEnv*) {
+  // Explicitly calling this is optional. Dlclose will take care of it as well.
+  sIcuRegistration.reset();
 }