Unload native libraries during shutdown.

Bug: 31550016
Test: two_runtimes_test.cc
Change-Id: Ic44fa57d01d5e00e08b06e3b05c86d44cb45c509
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 56c84a6..ae97907 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -638,6 +638,7 @@
         "subtype_check_test.cc",
         "thread_pool_test.cc",
         "transaction_test.cc",
+        "two_runtimes_test.cc",
         "vdex_file_test.cc",
         "verifier/method_verifier_test.cc",
         "verifier/reg_type_test.cc",
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index df96d28..a0c013c 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -224,6 +224,20 @@
     STLDeleteValues(&libraries_);
   }
 
+  void UnloadBootNativeLibraries(JavaVM* vm) {
+    std::vector<SharedLibrary*> unload_libraries;
+    {
+      MutexLock mu(Thread::Current(), *Locks::jni_libraries_lock_);
+      for (auto it = libraries_.begin(); it != libraries_.end(); ++it) {
+        SharedLibrary* const library = it->second;
+        if (library->GetClassLoader() == nullptr) {
+          unload_libraries.push_back(library);
+        }
+      }
+    }
+    UnloadLibraries(vm, unload_libraries);
+  }
+
   // NO_THREAD_SAFETY_ANALYSIS since this may be called from Dumpable. Dumpable can't be annotated
   // properly due to the template. The caller should be holding the jni_libraries_lock_.
   void Dump(std::ostream& os) const NO_THREAD_SAFETY_ANALYSIS {
@@ -337,17 +351,23 @@
     }
     ScopedThreadSuspension sts(self, kNative);
     // Do this without holding the jni libraries lock to prevent possible deadlocks.
-    using JNI_OnUnloadFn = void(*)(JavaVM*, void*);
+    UnloadLibraries(self->GetJniEnv()->GetVm(), unload_libraries);
     for (auto library : unload_libraries) {
+      delete library;
+    }
+  }
+
+  static void UnloadLibraries(JavaVM* vm, const std::vector<SharedLibrary*>& libraries) {
+    using JNI_OnUnloadFn = void(*)(JavaVM*, void*);
+    for (SharedLibrary* library : libraries) {
       void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
       if (sym == nullptr) {
         VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
       } else {
         VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling...";
         JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
-        jni_on_unload(self->GetJniEnv()->GetVm(), nullptr);
+        jni_on_unload(vm, nullptr);
       }
-      delete library;
     }
   }
 
@@ -856,6 +876,10 @@
   libraries_.get()->UnloadNativeLibraries();
 }
 
+void JavaVMExt::UnloadBootNativeLibraries() {
+  libraries_.get()->UnloadBootNativeLibraries(this);
+}
+
 bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                   const std::string& path,
                                   jobject class_loader,
diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h
index 424dd7c..6f7e546 100644
--- a/runtime/jni/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -109,6 +109,11 @@
       REQUIRES(!Locks::jni_libraries_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Unload all boot classpath native libraries.
+  void UnloadBootNativeLibraries()
+      REQUIRES(!Locks::jni_libraries_lock_)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   /**
    * Returns a pointer to the code for the native method 'm', found
    * using dlsym(3) on every native library that's been loaded so far.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8223fb8..9ab4047 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -394,6 +394,7 @@
                                             WellKnownClasses::java_lang_Daemons_stop);
   }
 
+  // Shutdown any trace running.
   Trace::Shutdown();
 
   // Report death. Clients me require a working thread, still, so do it before GC completes and
@@ -431,6 +432,10 @@
     thread_list_->ShutDown();
   }
 
+  // We can only unload boot classpath native libraries once all threads are terminated
+  // or suspended.
+  java_vm_->UnloadBootNativeLibraries();
+
   // TODO Maybe do some locking.
   for (auto& agent : agents_) {
     agent->Unload();
diff --git a/runtime/two_runtimes_test.cc b/runtime/two_runtimes_test.cc
new file mode 100644
index 0000000..b52f66a
--- /dev/null
+++ b/runtime/two_runtimes_test.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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 "dexopt_test.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+
+// This test creates two runtimes consecutively to check that state is
+// setup and cleaned-up correctly each time.
+
+// Make this a DexoptTest, which makes sure runtime images get mapped
+// at random addresses.
+class TwoRuntimesTest : public DexoptTest {};
+
+TEST_F(TwoRuntimesTest, FirstInvocation) {
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+}
+
+TEST_F(TwoRuntimesTest, SecondInvocation) {
+  Thread::Current()->TransitionFromSuspendedToRunnable();
+  runtime_->Start();
+}
+
+}  // namespace art
diff --git a/test/150-loadlibrary/expected.txt b/test/150-loadlibrary/expected.txt
index 41feacf..ecf40b6 100644
--- a/test/150-loadlibrary/expected.txt
+++ b/test/150-loadlibrary/expected.txt
@@ -1,2 +1,3 @@
 JNI_OnLoad called
 Success.
+JNI_OnUnload called