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);