libnativehelper: refactoring for Runtime Module

This change puts a C ABI between the caller and libnativehelper which
means each could link separate libc++ library instances using
namespaces.

Bug: b/119840313
Test: Builds & boots
Test: atest JniInvocation_test
Test: atest libnativehelper_api_test

Change-Id: Idaa4ddd3118cbb4f3997abdcf355220ef80eb775
diff --git a/Android.bp b/Android.bp
index e6b2916..6722c5e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -31,8 +31,8 @@
     export_include_dirs: ["platform_include"],
 }
 
-cc_library {
-    name: "libnativehelper",
+cc_defaults {
+    name: "libnativehelper_defaults",
     host_supported: true,
     srcs: [
         "JNIHelp.cpp",
@@ -40,24 +40,11 @@
         "JniInvocation.cpp",
         "toStringArray.cpp",
     ],
-
     target: {
         linux: {
             srcs: ["AsynchronousCloseMonitor.cpp"],
         },
     },
-
-    header_libs: [
-        "jni_headers",
-        "libnativehelper_header_only",
-        "jni_platform_headers"
-    ],
-    export_header_lib_headers: [
-        "jni_headers",
-        "libnativehelper_header_only",
-        "jni_platform_headers",
-    ],
-
     shared_libs: [
         "liblog",
     ],
@@ -65,8 +52,20 @@
         "-Werror",
         "-fvisibility=protected",
     ],
+    export_include_dirs: [
+         "include",
+         "header_only_include",
+         "platform_include"
+    ],
+}
 
-    export_include_dirs: ["include"],
+cc_library {
+    name: "libnativehelper",
+    defaults: [ "libnativehelper_defaults" ],
+    stubs: {
+       symbol_file: "libnativehelper.map.txt",
+       versions: ["1"],
+    },
 }
 
 //
diff --git a/AsynchronousCloseMonitor.cpp b/AsynchronousCloseMonitor.cpp
index 68b810d..d8c6ef8 100644
--- a/AsynchronousCloseMonitor.cpp
+++ b/AsynchronousCloseMonitor.cpp
@@ -26,6 +26,29 @@
 
 #include <mutex>
 
+namespace {
+
+class AsynchronousCloseMonitorImpl {
+public:
+    explicit AsynchronousCloseMonitorImpl(int fd);
+    ~AsynchronousCloseMonitorImpl();
+    bool wasSignaled() const;
+
+    static void init();
+
+    static void signalBlockedThreads(int fd);
+
+private:
+    AsynchronousCloseMonitorImpl(const AsynchronousCloseMonitorImpl&) = delete;
+    AsynchronousCloseMonitorImpl& operator=(const AsynchronousCloseMonitorImpl&) = delete;
+
+    AsynchronousCloseMonitorImpl* mPrev;
+    AsynchronousCloseMonitorImpl* mNext;
+    pthread_t mThread;
+    int mFd;
+    bool mSignaled;
+};
+
 /**
  * We use an intrusive doubly-linked list to keep track of blocked threads.
  * This gives us O(1) insertion and removal, and means we don't need to do any allocation.
@@ -35,7 +58,7 @@
  * question). For now at least, this seems like a good compromise for Android.
  */
 static std::mutex blockedThreadListMutex;
-static AsynchronousCloseMonitor* blockedThreadList = NULL;
+static AsynchronousCloseMonitorImpl* blockedThreadList = NULL;
 
 /**
  * The specific signal chosen here is arbitrary, but bionic needs to know so that SIGRTMIN
@@ -47,7 +70,7 @@
     // Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
 }
 
-void AsynchronousCloseMonitor::init() {
+void AsynchronousCloseMonitorImpl::init() {
     // Ensure that the signal we send interrupts system calls but doesn't kill threads.
     // Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
     // (The whole reason we're sending this signal is to unblock system calls!)
@@ -61,9 +84,9 @@
     }
 }
 
-void AsynchronousCloseMonitor::signalBlockedThreads(int fd) {
+void AsynchronousCloseMonitorImpl::signalBlockedThreads(int fd) {
     std::lock_guard<std::mutex> lock(blockedThreadListMutex);
-    for (AsynchronousCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
+    for (AsynchronousCloseMonitorImpl* it = blockedThreadList; it != NULL; it = it->mNext) {
         if (it->mFd == fd) {
             it->mSignaled = true;
             pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
@@ -72,11 +95,11 @@
     }
 }
 
-bool AsynchronousCloseMonitor::wasSignaled() const {
+bool AsynchronousCloseMonitorImpl::wasSignaled() const {
     return mSignaled;
 }
 
-AsynchronousCloseMonitor::AsynchronousCloseMonitor(int fd) {
+AsynchronousCloseMonitorImpl::AsynchronousCloseMonitorImpl(int fd) {
     std::lock_guard<std::mutex> lock(blockedThreadListMutex);
     // Who are we, and what are we waiting for?
     mThread = pthread_self();
@@ -91,7 +114,7 @@
     blockedThreadList = this;
 }
 
-AsynchronousCloseMonitor::~AsynchronousCloseMonitor() {
+AsynchronousCloseMonitorImpl::~AsynchronousCloseMonitorImpl() {
     std::lock_guard<std::mutex> lock(blockedThreadListMutex);
     // Unlink ourselves from the intrusive doubly-linked list...
     if (mNext != NULL) {
@@ -103,3 +126,31 @@
         mPrev->mNext = mNext;
     }
 }
+
+}  // namespace
+
+//
+// C ABI and API boundary
+//
+
+MODULE_API void async_close_monitor_static_init() {
+  AsynchronousCloseMonitorImpl::init();
+}
+
+MODULE_API void async_close_monitor_signal_blocked_threads(int fd) {
+  AsynchronousCloseMonitorImpl::signalBlockedThreads(fd);
+}
+
+MODULE_API void* async_close_monitor_create(int fd) {
+  return new AsynchronousCloseMonitorImpl(fd);
+}
+
+MODULE_API void async_close_monitor_destroy(void* instance) {
+  auto monitor = reinterpret_cast<AsynchronousCloseMonitorImpl*>(instance);
+  delete monitor;
+}
+
+MODULE_API int async_close_monitor_was_signalled(const void* instance) {
+  auto monitor = reinterpret_cast<const AsynchronousCloseMonitorImpl*>(instance);
+  return monitor->wasSignaled() ? 1 : 0;
+}
diff --git a/JNIHelp.cpp b/JNIHelp.cpp
index eca2abd..0cd048d 100644
--- a/JNIHelp.cpp
+++ b/JNIHelp.cpp
@@ -63,7 +63,7 @@
     return (*env)->FindClass(e, className);
 }
 
-extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
+MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
     const JNINativeMethod* gMethods, int numMethods)
 {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
@@ -214,7 +214,7 @@
     return true;
 }
 
-extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
+MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
 
     if ((*env)->ExceptionCheck(e)) {
@@ -245,21 +245,21 @@
     return 0;
 }
 
-int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
+MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
     char msgBuf[512];
     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
     return jniThrowException(env, className, msgBuf);
 }
 
-int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
+MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
     return jniThrowException(env, "java/lang/NullPointerException", msg);
 }
 
-int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
+MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
     return jniThrowException(env, "java/lang/RuntimeException", msg);
 }
 
-int jniThrowIOException(C_JNIEnv* env, int errnum) {
+MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum) {
     char buffer[80];
     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
     return jniThrowException(env, "java/io/IOException", message);
@@ -293,7 +293,7 @@
     return trace;
 }
 
-void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
+MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
     std::string trace(jniGetStackTrace(env, exception));
     __android_log_write(priority, tag, trace.c_str());
 }
@@ -326,13 +326,13 @@
 
 }  // namespace impl
 
-const char* jniStrError(int errnum, char* buf, size_t buflen) {
+MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen) {
   // The magic of C++ overloading selects the correct implementation based on the declared type of
   // strerror_r. The inline will ensure that we don't have any indirect calls.
   return impl::realJniStrError(strerror_r, errnum, buf, buflen);
 }
 
-jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
+MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e),
                                           JniConstants::GetFileDescriptorInitMethod(e));
@@ -344,7 +344,7 @@
     return fileDescriptor;
 }
 
-int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
+MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     if (fileDescriptor != nullptr) {
         return e->GetIntField(fileDescriptor,
@@ -354,7 +354,7 @@
     }
 }
 
-void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
+MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     if (fileDescriptor == nullptr) {
         jniThrowNullPointerException(e, "null FileDescriptor");
@@ -363,17 +363,17 @@
     }
 }
 
-jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
+MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e));
 }
 
-jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
+MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e));
 }
 
-jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
+MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
     return e->NewString(unicodeChars, len);
 }
diff --git a/JniInvocation.cpp b/JniInvocation.cpp
index 88c590a..d2229b3 100644
--- a/JniInvocation.cpp
+++ b/JniInvocation.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <nativehelper/JniInvocation.h>
+#include "nativehelper/JniInvocation.h"
 
 #include <dlfcn.h>
 #include <stdlib.h>
@@ -31,6 +31,8 @@
 
 #include "JniConstants.h"
 
+namespace {
+
 template <typename T>
 void UNUSED(const T&) {}
 
@@ -53,19 +55,62 @@
 #endif
 }
 
-JniInvocation* JniInvocation::jni_invocation_ = NULL;
+}  // namespace
 
-JniInvocation::JniInvocation() :
+struct JniInvocationImpl final {
+ public:
+  JniInvocationImpl();
+  ~JniInvocationImpl();
+
+  bool Init(const char* library);
+
+  //  static const char* GetLibrary(const char* library, char* buffer);
+
+  static const char* GetLibrary(const char* library,
+                                char* buffer,
+                                bool (*is_debuggable)() = IsDebuggable,
+                                int (*get_library_system_property)(char* buffer) = GetLibrarySystemProperty);
+
+  static JniInvocationImpl& GetJniInvocation();
+
+  jint JNI_GetDefaultJavaVMInitArgs(void* vmargs);
+  jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
+  jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count);
+
+ private:
+  JniInvocationImpl(const JniInvocationImpl&) = delete;
+  JniInvocationImpl& operator=(const JniInvocationImpl&) = delete;
+
+  bool FindSymbol(void** pointer, const char* symbol);
+
+  static JniInvocationImpl* jni_invocation_;
+
+  // Handle to library opened with dlopen(). Library exports
+  // JNI_GetDefaultJavaVMInitArgs, JNI_CreateJavaVM, JNI_GetCreatedJavaVMs.
+  void* handle_;
+  jint (*JNI_GetDefaultJavaVMInitArgs_)(void*);
+  jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);
+  jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*);
+
+  friend class JNIInvocation_Debuggable_Test;
+  friend class JNIInvocation_NonDebuggable_Test;
+};
+
+// Check JniInvocationImpl size is same as fields, e.g. no vtable present.
+static_assert(sizeof(JniInvocationImpl) == 4 * sizeof(uintptr_t));
+
+JniInvocationImpl* JniInvocationImpl::jni_invocation_ = NULL;
+
+JniInvocationImpl::JniInvocationImpl() :
     handle_(NULL),
     JNI_GetDefaultJavaVMInitArgs_(NULL),
     JNI_CreateJavaVM_(NULL),
     JNI_GetCreatedJavaVMs_(NULL) {
-
   LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
   jni_invocation_ = this;
 }
 
-JniInvocation::~JniInvocation() {
+JniInvocationImpl::~JniInvocationImpl() {
   jni_invocation_ = NULL;
   if (handle_ != NULL) {
     dlclose(handle_);
@@ -74,12 +119,10 @@
 
 static const char* kLibraryFallback = "libart.so";
 
-const char* JniInvocation::GetLibrary(const char* library, char* buffer) {
-  return GetLibrary(library, buffer, &IsDebuggable, &GetLibrarySystemProperty);
-}
-
-const char* JniInvocation::GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(),
-                                      int (*get_library_system_property)(char* buffer)) {
+const char* JniInvocationImpl::GetLibrary(const char* library,
+                                          char* buffer,
+                                          bool (*is_debuggable)(),
+                                          int (*get_library_system_property)(char* buffer)) {
 #ifdef __ANDROID__
   const char* default_library;
 
@@ -118,7 +161,7 @@
   return library;
 }
 
-bool JniInvocation::Init(const char* library) {
+bool JniInvocationImpl::Init(const char* library) {
 #ifdef __ANDROID__
   char buffer[PROP_VALUE_MAX];
 #else
@@ -166,19 +209,19 @@
   return true;
 }
 
-jint JniInvocation::JNI_GetDefaultJavaVMInitArgs(void* vmargs) {
+jint JniInvocationImpl::JNI_GetDefaultJavaVMInitArgs(void* vmargs) {
   return JNI_GetDefaultJavaVMInitArgs_(vmargs);
 }
 
-jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
   return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
 }
 
-jint JniInvocation::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
+jint JniInvocationImpl::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
   return JNI_GetCreatedJavaVMs_(vms, size, vm_count);
 }
 
-bool JniInvocation::FindSymbol(void** pointer, const char* symbol) {
+bool JniInvocationImpl::FindSymbol(void** pointer, const char* symbol) {
   *pointer = dlsym(handle_, symbol);
   if (*pointer == NULL) {
     ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror());
@@ -189,24 +232,47 @@
   return true;
 }
 
-JniInvocation& JniInvocation::GetJniInvocation() {
+JniInvocationImpl& JniInvocationImpl::GetJniInvocation() {
   LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL,
                       "Failed to create JniInvocation instance before using JNI invocation API");
   return *jni_invocation_;
 }
 
-extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
-  return JniInvocation::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args);
+MODULE_API jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
+  return JniInvocationImpl::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args);
 }
 
-extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+MODULE_API jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
   // Ensure any cached heap objects from previous VM instances are
   // invalidated. There is no notification here that a VM is destroyed. These
   // cached objects limit us to one VM instance per process.
   JniConstants::Uninitialize();
-  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
+  return JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
 }
 
-extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
-  return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
+MODULE_API jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
+  return JniInvocationImpl::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
+}
+
+MODULE_API JniInvocationImpl* JniInvocationCreate() {
+  return new JniInvocationImpl();
+}
+
+MODULE_API void JniInvocationDestroy(JniInvocationImpl* instance) {
+  delete instance;
+}
+
+MODULE_API int JniInvocationInit(JniInvocationImpl* instance, const char* library) {
+  return instance->Init(library) ? 1 : 0;
+}
+
+MODULE_API const char* JniInvocationGetLibrary(const char* library, char* buffer) {
+  return JniInvocationImpl::GetLibrary(library, buffer);
+}
+
+MODULE_API const char* JniInvocation::GetLibrary(const char* library,
+                                                 char* buffer,
+                                                 bool (*is_debuggable)(),
+                                                 int (*get_library_system_property)(char* buffer)) {
+  return JniInvocationImpl::GetLibrary(library, buffer, is_debuggable, get_library_system_property);
 }
diff --git a/include/nativehelper/AsynchronousCloseMonitor.h b/include/nativehelper/AsynchronousCloseMonitor.h
index 2afc35c..e172ac4 100644
--- a/include/nativehelper/AsynchronousCloseMonitor.h
+++ b/include/nativehelper/AsynchronousCloseMonitor.h
@@ -19,6 +19,17 @@
 
 #include <pthread.h>
 
+#include "module_api.h"
+
+// Public API for library function.
+MODULE_API void async_close_monitor_destroy(void* instance);
+MODULE_API void async_close_monitor_static_init();
+MODULE_API void async_close_monitor_signal_blocked_threads(int fd);
+MODULE_API int async_close_monitor_was_signalled(const void* instance);
+MODULE_API void* async_close_monitor_create(int fd);
+
+#ifdef __cplusplus
+
 /**
  * AsynchronousCloseMonitor helps implement Java's asynchronous close semantics.
  *
@@ -42,24 +53,31 @@
  */
 class AsynchronousCloseMonitor {
 public:
-    explicit AsynchronousCloseMonitor(int fd);
-    ~AsynchronousCloseMonitor();
-    bool wasSignaled() const;
+    explicit AsynchronousCloseMonitor(int fd) {
+        instance_ = async_close_monitor_create(fd);
+    }
+    ~AsynchronousCloseMonitor() {
+        async_close_monitor_destroy(instance_);
+    }
+    bool wasSignaled() const {
+        return async_close_monitor_was_signalled(instance_) != 0;
+    }
 
-    static void init();
+    static void init() {
+        async_close_monitor_static_init();
+    }
 
-    static void signalBlockedThreads(int fd);
+    static void signalBlockedThreads(int fd) {
+        async_close_monitor_signal_blocked_threads(fd);
+    }
 
 private:
-    AsynchronousCloseMonitor* mPrev;
-    AsynchronousCloseMonitor* mNext;
-    pthread_t mThread;
-    int mFd;
-    bool mSignaled;
+    AsynchronousCloseMonitor(const AsynchronousCloseMonitor&) = delete;
+    AsynchronousCloseMonitor& operator=(const AsynchronousCloseMonitor&) = delete;
 
-    // Disallow copy and assignment.
-    AsynchronousCloseMonitor(const AsynchronousCloseMonitor&);
-    void operator=(const AsynchronousCloseMonitor&);
+    void* instance_;
 };
 
+#endif  // __cplusplus
+
 #endif  // ASYNCHRONOUS_CLOSE_MONITOR_H_included
diff --git a/include/nativehelper/JNIHelp.h b/include/nativehelper/JNIHelp.h
index d70fdad..08616b7 100644
--- a/include/nativehelper/JNIHelp.h
+++ b/include/nativehelper/JNIHelp.h
@@ -19,30 +19,29 @@
  *
  * This file may be included by C or C++ code, which is trouble because jni.h
  * uses different typedefs for JNIEnv in each language.
- *
- * TODO: remove C support.
  */
 #ifndef NATIVEHELPER_JNIHELP_H_
 #define NATIVEHELPER_JNIHELP_H_
 
-#include "jni.h"
 #include <errno.h>
 #include <unistd.h>
 
+#include <jni.h>
+#include "module_api.h"
+
 #ifndef NELEM
 # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
 #endif
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /*
  * Register one or more native methods with a particular class.
  * "className" looks like "java/lang/String". Aborts on failure.
  * TODO: fix all callers and change the return type to void.
  */
-int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
+MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env,
+                                        const char* className,
+                                        const JNINativeMethod* gMethods,
+                                        int numMethods);
 
 /*
  * Throw an exception with the specified class and an optional message.
@@ -58,22 +57,38 @@
  *
  * Currently aborts the VM if it can't throw the exception.
  */
-int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+
+/*
+ * Throw an exception with the specified class and formatted error message.
+ *
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
+ *
+ * If an exception is currently pending, we log a warning message and
+ * clear it.
+ *
+ * Returns 0 on success, nonzero if something failed (e.g. the exception
+ * class couldn't be found, so *an* exception will still be pending).
+ *
+ * Currently aborts the VM if it can't throw the exception.
+ */
+MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
 
 /*
  * Throw a java.lang.NullPointerException, with an optional message.
  */
-int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
+MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
 
 /*
  * Throw a java.lang.RuntimeException, with an optional message.
  */
-int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
+MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
 
 /*
  * Throw a java.io.IOException, generating the message from errno.
  */
-int jniThrowIOException(C_JNIEnv* env, int errnum);
+MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum);
 
 /*
  * Return a pointer to a locale-dependent error string explaining errno
@@ -81,56 +96,54 @@
  * This function is thread-safe (unlike strerror) and portable (unlike
  * strerror_r).
  */
-const char* jniStrError(int errnum, char* buf, size_t buflen);
+MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen);
 
 /*
  * Returns a new java.io.FileDescriptor for the given int fd.
  */
-jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
+MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
 
 /*
  * Returns the int fd from a java.io.FileDescriptor.
  */
-int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
 
 /*
  * Sets the int fd in a java.io.FileDescriptor.  Throws java.lang.NullPointerException
  * if fileDescriptor is null.
  */
-void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
+MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env,
+                                         jobject fileDescriptor,
+                                         int value);
 
 /*
  * Returns the long ownerId from a java.io.FileDescriptor.
  */
-jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
 
 /*
  * Returns the reference from a java.lang.ref.Reference.
  */
-jobject jniGetReferent(C_JNIEnv* env, jobject ref);
+MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref);
 
 /*
  * Returns a Java String object created from UTF-16 data either from jchar or,
  * if called from C++11, char16_t (a bitwise identical distinct type).
  */
-jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len);
+MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len);
 
 /*
  * Log a message and an exception.
  * If exception is NULL, logs the current exception in the JNI environment.
  */
-void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
-
-#ifdef __cplusplus
-}
-#endif
-
+MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
 
 /*
  * For C++ code, we provide inlines that map to the C functions.  g++ always
  * inlines these, even on non-optimized builds.
  */
 #if defined(__cplusplus)
+
 inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
     return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
 }
@@ -139,8 +152,6 @@
     return jniThrowException(&env->functions, className, msg);
 }
 
-extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
-
 /*
  * Equivalent to jniThrowException but with a printf-like format string and
  * variable-length argument list. This is only available in C++.
@@ -204,7 +215,7 @@
   void operator=(const TypeName&) = delete
 #endif  // !defined(DISALLOW_COPY_AND_ASSIGN)
 
-#endif
+#endif  // defined(__cplusplus)
 
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
diff --git a/include/nativehelper/JniInvocation.h b/include/nativehelper/JniInvocation.h
index 58beec5..0d87aa9 100644
--- a/include/nativehelper/JniInvocation.h
+++ b/include/nativehelper/JniInvocation.h
@@ -18,6 +18,16 @@
 #define JNI_INVOCATION_H_included
 
 #include <jni.h>
+#include "module_api.h"
+
+struct JniInvocationImpl;
+
+MODULE_API struct JniInvocationImpl* JniInvocationCreate();
+MODULE_API void JniInvocationDestroy(struct JniInvocationImpl* instance);
+MODULE_API int JniInvocationInit(struct JniInvocationImpl* instance, const char* library);
+MODULE_API const char* JniInvocationGetLibrary(const char* library, char* buffer);
+
+#ifdef __cplusplus
 
 // JniInvocation adds a layer of indirection for applications using
 // the JNI invocation API to allow the JNI implementation to be
@@ -26,48 +36,45 @@
 // library will chosen based on the value of Android system property
 // persist.sys.dalvik.vm.lib on the device, and otherwise fall back to
 // a hard-coded default implementation.
-class JniInvocation {
+class JniInvocation final {
  public:
-  JniInvocation();
+  JniInvocation() {
+    impl_ = JniInvocationCreate();
+  }
 
-  ~JniInvocation();
+  ~JniInvocation() {
+    JniInvocationDestroy(impl_);
+  }
 
   // Initialize JNI invocation API. library should specifiy a valid
   // shared library for opening via dlopen providing a JNI invocation
   // implementation, or null to allow defaulting via
   // persist.sys.dalvik.vm.lib.
-  bool Init(const char* library);
+  bool Init(const char* library) {
+    return JniInvocationInit(impl_, library) != 0;
+  }
 
   // Exposes which library is actually loaded from the given name. The
   // buffer of size PROPERTY_VALUE_MAX will be used to load the system
   // property for the default library, if necessary. If no buffer is
   // provided, the fallback value will be used.
-  static const char* GetLibrary(const char* library, char* buffer);
+  static const char* GetLibrary(const char* library, char* buffer) {
+    return JniInvocationGetLibrary(library, buffer);
+  }
 
  private:
+  JniInvocation(const JniInvocation&) = delete;
+  JniInvocation& operator=(const JniInvocation&) = delete;
+
   static const char* GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(),
                                 int (*get_library_system_property)(char* buffer));
 
-  bool FindSymbol(void** pointer, const char* symbol);
+  JniInvocationImpl* impl_;
 
-  static JniInvocation& GetJniInvocation();
-
-  jint JNI_GetDefaultJavaVMInitArgs(void* vmargs);
-  jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
-  jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count);
-
-  static JniInvocation* jni_invocation_;
-
-  void* handle_;
-  jint (*JNI_GetDefaultJavaVMInitArgs_)(void*);
-  jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);
-  jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*);
-
-  friend jint JNI_GetDefaultJavaVMInitArgs(void* vm_args);
-  friend jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
-  friend jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count);
   friend class JNIInvocation_Debuggable_Test;
   friend class JNIInvocation_NonDebuggable_Test;
 };
 
+#endif  // __cplusplus
+
 #endif  // JNI_INVOCATION_H_included
diff --git a/include/nativehelper/module_api.h b/include/nativehelper/module_api.h
new file mode 100644
index 0000000..8b109e3
--- /dev/null
+++ b/include/nativehelper/module_api.h
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+#define MODULE_API extern "C"
+#else
+#define MODULE_API
+#endif  // __cplusplus
diff --git a/include/nativehelper/toStringArray.h b/include/nativehelper/toStringArray.h
index cb46024..1965d6a 100644
--- a/include/nativehelper/toStringArray.h
+++ b/include/nativehelper/toStringArray.h
@@ -17,13 +17,20 @@
 #ifndef TO_STRING_ARRAY_H_included
 #define TO_STRING_ARRAY_H_included
 
-#include "jni.h"
-#include "ScopedLocalRef.h"
+#include <stddef.h>
+
+#include <jni.h>
+#include "module_api.h"
+
+// Public API for libnativehelper library.
+MODULE_API jobjectArray newStringArray(JNIEnv* env, size_t count);
+MODULE_API jobjectArray toStringArray(JNIEnv* env, const char* const* strings);
+
+#ifdef __cplusplus
 
 #include <string>
 #include <vector>
-
-jobjectArray newStringArray(JNIEnv* env, size_t count);
+#include "ScopedLocalRef.h"
 
 template <typename Counter, typename Getter>
 jobjectArray toStringArray(JNIEnv* env, Counter* counter, Getter* getter) {
@@ -66,6 +73,6 @@
     return toStringArray<VectorCounter, VectorGetter>(env, &counter, &getter);
 }
 
-JNIEXPORT jobjectArray toStringArray(JNIEnv* env, const char* const* strings);
+#endif  // __cplusplus
 
 #endif  // TO_STRING_ARRAY_H_included
diff --git a/libnativehelper.map.txt b/libnativehelper.map.txt
new file mode 100644
index 0000000..9e03268
--- /dev/null
+++ b/libnativehelper.map.txt
@@ -0,0 +1,41 @@
+# This library should only export C linkage definitions.
+#
+# VERSION string that follows is derived from <library_name>_<version>.
+LIBNATIVEHELPER_1 {
+  global:
+    JNI_GetDefaultJavaVMInitArgs;
+    JNI_CreateJavaVM;
+    JNI_GetCreatedJavaVMs;
+
+    jniRegisterNativeMethods;
+    jniThrowException;
+    jniThrowExceptionFmt;
+    jniThrowNullPointerException;
+    jniThrowRuntimeException;
+    jniThrowIOException;
+    jniStrError;
+    jniCreateFileDescriptor;
+    jniGetFDFromFileDescriptor;
+    jniSetFileDescriptorOfFD;
+    jniGetOwnerIdFromFileDescriptor;
+    jniGetReferent;
+    jniCreateString;
+    jniLogException;
+
+    JniInvocationCreate;
+    JniInvocationDestroy;
+    JniInvocationInit;
+    JniInvocationGetLibrary;
+
+    async_close_monitor_static_init;
+    async_close_monitor_signal_blocked_threads;
+    async_close_monitor_create;
+    async_close_monitor_destroy;
+    async_close_monitor_was_signalled;
+
+    newStringArray;
+    toStringArray;
+
+  local:
+    *;
+};
diff --git a/tests/Android.bp b/tests/Android.bp
index 31a9583..4d90c97 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -60,3 +60,12 @@
 
   shared_libs: ["libnativehelper"],
 }
+
+cc_test {
+  name: "libnativehelper_api_test",
+  host_supported: true,
+  cflags: ["-Wall", "-Werror"],
+  srcs: ["libnativehelper_api_test.c"],  // C Compilation test.
+  tidy: true,
+  shared_libs: ["libnativehelper"],
+}
\ No newline at end of file
diff --git a/tests/libnativehelper_api_test.c b/tests/libnativehelper_api_test.c
new file mode 100644
index 0000000..cf2930c
--- /dev/null
+++ b/tests/libnativehelper_api_test.c
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+// All header files with MODULE_API decorated function declarations.
+#include "nativehelper/AsynchronousCloseMonitor.h"
+#include "nativehelper/JNIHelp.h"
+#include "nativehelper/JniInvocation.h"
+#include "nativehelper/toStringArray.h"
+
+int main() {
+  // The test here is that the headers are properly guarded to support
+  // compilation with a C compiler.
+  return 0;
+}
diff --git a/toStringArray.cpp b/toStringArray.cpp
index f7defdc..b1f0f42 100644
--- a/toStringArray.cpp
+++ b/toStringArray.cpp
@@ -18,9 +18,7 @@
 
 #include "JniConstants.h"
 
-jobjectArray newStringArray(JNIEnv* env, size_t count) {
-    return env->NewObjectArray(count, JniConstants::GetStringClass(env), nullptr);
-}
+namespace {
 
 struct ArrayCounter {
     const char* const* strings;
@@ -42,7 +40,13 @@
     }
 };
 
-jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
+}  // namespace
+
+MODULE_API jobjectArray newStringArray(JNIEnv* env, size_t count) {
+    return env->NewObjectArray(count, JniConstants::GetStringClass(env), nullptr);
+}
+
+MODULE_API jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
     ArrayCounter counter(strings);
     ArrayGetter getter(strings);
     return toStringArray(env, &counter, &getter);