android 13 from xiaosuan

This commit is contained in:
cpeng 2025-08-25 08:35:43 +08:00
commit b23f8b6eeb
65 changed files with 9182 additions and 0 deletions

78
ALog-priv.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright 2013 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
#include <android/log.h>
#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#endif
/*
* Basic log message macros intended to emulate the behavior of log/log.h
* in system core. This should be dependent only on ndk exposed logging
* functionality.
*/
#ifndef ALOG
#define ALOG(priority, tag, fmt, ...) \
__android_log_print(ANDROID_##priority, tag, fmt, __VA_ARGS__)
#endif
#ifndef ALOGV
#if LOG_NDEBUG
#define ALOGV(...) ((void)0)
#else
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#ifndef ALOGD
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGI
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGW
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef ALOGF
#define ALOGF(...) ((void)ALOG(LOG_FATAL, LOG_TAG, __VA_ARGS__))
#endif
/*
* Log a fatal error if cond is true. The condition test is inverted from
* assert(3) semantics. The test and message are not stripped from release
* builds
*/
#ifndef ALOG_ALWAYS_FATAL_IF
#define ALOG_ALWAYS_FATAL_IF(cond, ...) \
if (cond) __android_log_assert(#cond, LOG_TAG, __VA_ARGS__)
#endif

248
Android.bp Normal file
View File

@ -0,0 +1,248 @@
// Copyright (C) 2009 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.
package {
default_applicable_licenses: ["libnativehelper_license"],
}
// Added automatically by a large-scale-change
// http://go/android-license-faq
license {
name: "libnativehelper_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
],
license_text: [
"NOTICE",
],
}
cc_defaults {
name: "libnativehelper_defaults",
cflags: [
"-fvisibility=protected",
"-std=c11",
],
shared_libs: ["liblog"],
}
cc_library_headers {
name: "jni_headers",
host_supported: true,
export_include_dirs: ["include_jni"],
native_bridge_supported: true,
vendor_available: true,
target: {
windows: {
enabled: true,
},
},
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
ramdisk_available: true,
// recovery_available currently required for libchrome (https://r.android.com/799940).
recovery_available: true,
visibility: ["//visibility:public"],
stl: "none",
system_shared_libs: [],
// The minimum sdk version required by users of this module.
sdk_version: "minimum",
// As part of mainline modules(APEX), it should support at least 29(Q)
min_sdk_version: "29",
}
cc_library_headers {
name: "libnativehelper_header_only",
host_supported: true,
export_include_dirs: [
"header_only_include",
],
header_libs: ["jni_headers"],
export_header_lib_headers: ["jni_headers"],
// As part of mainline modules(APEX), it should support at least 29(Q)
min_sdk_version: "29",
sdk_version: "minimum",
apex_available: [
"//apex_available:platform",
"//apex_available:anyapex",
],
}
cc_library_headers {
name: "jni_platform_headers",
host_supported: true,
export_include_dirs: [
"include_platform_header_only",
],
header_libs: ["jni_headers"],
export_header_lib_headers: ["jni_headers"],
sdk_version: "minimum",
apex_available: [
"//apex_available:platform",
"com.android.art",
"com.android.art.debug",
],
min_sdk_version: "S",
}
cc_library_shared {
name: "libnativehelper",
defaults: ["libnativehelper_defaults"],
bootstrap: false,
host_supported: true,
srcs: [
"DlHelp.c",
"ExpandableString.c",
"JNIHelp.c",
"JNIPlatformHelp.c",
"JniConstants.c",
"JniInvocation.c",
"file_descriptor_jni.c",
],
export_include_dirs: [
"header_only_include",
"include",
"include_jni",
"include_platform",
"include_platform_header_only",
],
stl: "none",
stubs: {
symbol_file: "libnativehelper.map.txt",
versions: ["S"],
},
// Only distributed in the ART Module.
apex_available: [
"com.android.art",
"com.android.art.debug",
],
min_sdk_version: "S",
}
// Lazy loading version of libnativehelper that can be used by code
// that is running before the ART APEX is mounted and
// libnativehelper.so is available.
cc_library_static {
name: "libnativehelper_lazy",
defaults: ["libnativehelper_defaults"],
bootstrap: true,
host_supported: true,
export_include_dirs: [
"header_only_include",
"include",
"include_jni",
"include_platform",
"include_platform_header_only",
],
apex_available: ["//apex_available:platform"],
srcs: ["libnativehelper_lazy.c"],
target: {
linux: {
version_script: "libnativehelper.map.txt",
},
},
}
//
// NDK-only build for the target (device), using libc++.
// - Relies only on NDK exposed functionality.
// - This doesn't include JniInvocation.
//
cc_library {
name: "libnativehelper_compat_libc++",
defaults: ["libnativehelper_defaults"],
header_libs: ["jni_headers"],
cflags: ["-Werror"],
export_header_lib_headers: ["jni_headers"],
export_include_dirs: [
"header_only_include",
"include",
],
host_supported: true,
local_include_dirs: [
"header_only_include",
"include_platform_header_only",
],
srcs: [
"ExpandableString.c",
"JNIHelp.c",
],
min_sdk_version: "29",
sdk_version: "19",
stl: "none",
apex_available: [
"//apex_available:platform",
"com.android.art",
"com.android.art.debug",
"com.android.extservices",
"com.android.tethering",
],
visibility: [
"//art:__subpackages__",
"//cts:__subpackages__",
"//external/perfetto:__subpackages__",
"//frameworks/base/packages/Connectivity/tests/integration:__pkg__",
"//frameworks/base/packages/ConnectivityT:__subpackages__", // TODO: remove after code move
"//frameworks/base/packages/Tethering:__subpackages__",
"//frameworks/libs/net/common/native/bpfmapjni",
"//frameworks/libs/net/common/native/bpfutiljni",
"//libcore:__subpackages__",
"//packages/modules/Connectivity:__subpackages__",
"//packages/modules/ExtServices:__subpackages__",
"//packages/modules/NetworkStack:__subpackages__",
":__subpackages__",
],
}
// The NDK module definitions reside in
// system/extras/module_ndk_libs/libnativehelper in platform, with copies of
// these headers and map.txt. Any changes here should be synced there and vice
// versa.
//
// TODO(b/170644498): Improve tooling to remove this duplication.
//
// ndk_headers {
// name: "ndk_jni.h",
// from: "include_jni",
// to: "",
// srcs: ["include_jni/jni.h"],
// license: "NOTICE",
// }
//
// ndk_headers {
// name: "libnativehelper_ndk_headers",
// from: "include",
// to: "",
// srcs: ["include/android/*.h"],
// license: "NOTICE",
// }
//
// ndk_library {
// name: "libnativehelper",
// symbol_file: "libnativehelper.map.txt",
// first_version: "S",
// }
//
// Tests.
//
subdirs = [
"tests",
"tests_mts",
]

82
DlHelp.c Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 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 "DlHelp.h"
#include <stdbool.h>
#ifdef WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <windows.h>
#else
#include <dlfcn.h>
#endif
DlLibrary DlOpenLibrary(const char* filename) {
#ifdef _WIN32
return LoadLibrary(filename);
#else
// Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
// This is due to the fact that it is possible that some threads might have yet to finish
// exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
// unloaded.
return dlopen(filename, RTLD_NOW | RTLD_NODELETE);
#endif
}
bool DlCloseLibrary(DlLibrary library) {
#ifdef _WIN32
return (FreeLibrary(library) == TRUE);
#else
return (dlclose(library) == 0);
#endif
}
DlSymbol DlGetSymbol(DlLibrary handle, const char* symbol) {
#ifdef _WIN32
return (DlSymbol) GetProcAddress(handle, symbol);
#else
return dlsym(handle, symbol);
#endif
}
const char* DlGetError() {
#ifdef _WIN32
static char buffer[256];
DWORD cause = GetLastError();
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
DWORD length = FormatMessageA(flags, NULL, cause, 0, buffer, sizeof(buffer), NULL);
if (length == 0) {
snprintf(buffer, sizeof(buffer),
"Error %lu while retrieving message for error %lu",
GetLastError(), cause);
length = strlen(buffer);
}
// Trim trailing whitespace.
for (DWORD i = length - 1; i > 0; --i) {
if (!isspace(buffer[i])) {
break;
}
buffer[i] = '\0';
}
return buffer;
#else
return dlerror();
#endif
}

32
DlHelp.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2020 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
#include <sys/cdefs.h>
#include <stdbool.h>
__BEGIN_DECLS
typedef void* DlLibrary;
typedef void* DlSymbol;
DlLibrary DlOpenLibrary(const char* filename);
bool DlCloseLibrary(DlLibrary library);
DlSymbol DlGetSymbol(DlLibrary library, const char* symbol);
const char* DlGetError();
__END_DECLS

48
ExpandableString.c Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2020 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 "ExpandableString.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ExpandableStringInitialize(struct ExpandableString *s) {
memset(s, 0, sizeof(*s));
}
void ExpandableStringRelease(struct ExpandableString* s) {
free(s->data);
memset(s, 0, sizeof(*s));
}
bool ExpandableStringAppend(struct ExpandableString* s, const char* text) {
size_t textSize = strlen(text);
size_t requiredSize = s->dataSize + textSize + 1;
char* data = (char*) realloc(s->data, requiredSize);
if (data == NULL) {
return false;
}
s->data = data;
memcpy(s->data + s->dataSize, text, textSize + 1);
s->dataSize += textSize;
return true;
}
bool ExpandableStringAssign(struct ExpandableString* s, const char* text) {
ExpandableStringRelease(s);
return ExpandableStringAppend(s, text);
}

45
ExpandableString.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2020 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
#include <sys/cdefs.h>
#include <stdbool.h>
#include <stddef.h>
__BEGIN_DECLS
struct ExpandableString {
size_t dataSize; // The length of the C string data (not including the null-terminator).
char* data; // The C string data.
};
// Initialize ExpandableString.
void ExpandableStringInitialize(struct ExpandableString* s);
// Release memory associated with ExpandableString.
void ExpandableStringRelease(struct ExpandableString* s);
// Append null-terminated string |text| to ExpandableString, expanding the storage if required.
// Returns true on success, false othewise.
bool ExpandableStringAppend(struct ExpandableString* s, const char* text);
// Assign null-terminate string |text| to ExpandableString, expanding the storage if required.
// Returns true on success, false othewise.
bool ExpandableStringAssign(struct ExpandableString*s, const char* text);
__END_DECLS

389
JNIHelp.c Normal file
View File

@ -0,0 +1,389 @@
/*
* Copyright (C) 2006 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 "include/nativehelper/JNIHelp.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#define LOG_TAG "JNIHelp"
#include "ALog-priv.h"
#include "ExpandableString.h"
//
// Helper methods
//
static const char* platformStrError(int errnum, char* buf, size_t buflen) {
#ifdef _WIN32
strerror_s(buf, buflen, errnum);
return buf;
#elif defined(__USE_GNU) && __ANDROID_API__ >= 23
// char *strerror_r(int errnum, char *buf, size_t buflen); /* GNU-specific */
return strerror_r(errnum, buf, buflen);
#else
// int strerror_r(int errnum, char *buf, size_t buflen); /* XSI-compliant */
int rc = strerror_r(errnum, buf, buflen);
if (rc != 0) {
snprintf(buf, buflen, "errno %d", errnum);
}
return buf;
#endif
}
static jmethodID FindMethod(JNIEnv* env,
const char* className,
const char* methodName,
const char* descriptor) {
// This method is only valid for classes in the core library which are
// not unloaded during the lifetime of managed code execution.
jclass clazz = (*env)->FindClass(env, className);
jmethodID methodId = (*env)->GetMethodID(env, clazz, methodName, descriptor);
(*env)->DeleteLocalRef(env, clazz);
return methodId;
}
static bool AppendJString(JNIEnv* env, jstring text, struct ExpandableString* dst) {
const char* utfText = (*env)->GetStringUTFChars(env, text, NULL);
if (utfText == NULL) {
return false;
}
bool success = ExpandableStringAppend(dst, utfText);
(*env)->ReleaseStringUTFChars(env, text, utfText);
return success;
}
/*
* Returns a human-readable summary of an exception object. The buffer will
* be populated with the "binary" class name and, if present, the
* exception message.
*/
static bool GetExceptionSummary(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
// Summary is <exception_class_name> ": " <exception_message>
jclass exceptionClass = (*env)->GetObjectClass(env, thrown); // Always succeeds
jmethodID getName = FindMethod(env, "java/lang/Class", "getName", "()Ljava/lang/String;");
jstring className = (jstring) (*env)->CallObjectMethod(env, exceptionClass, getName);
if (className == NULL) {
ExpandableStringAssign(dst, "<error getting class name>");
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, exceptionClass);
return false;
}
(*env)->DeleteLocalRef(env, exceptionClass);
exceptionClass = NULL;
if (!AppendJString(env, className, dst)) {
ExpandableStringAssign(dst, "<error getting class name UTF-8>");
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, className);
return false;
}
(*env)->DeleteLocalRef(env, className);
className = NULL;
jmethodID getMessage =
FindMethod(env, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;");
jstring message = (jstring) (*env)->CallObjectMethod(env, thrown, getMessage);
if (message == NULL) {
return true;
}
bool success = (ExpandableStringAppend(dst, ": ") && AppendJString(env, message, dst));
if (!success) {
// Two potential reasons for reaching here:
//
// 1. managed heap allocation failure (OOME).
// 2. native heap allocation failure for the storage in |dst|.
//
// Attempt to append failure notification, okay to fail, |dst| contains the class name
// of |thrown|.
ExpandableStringAppend(dst, "<error getting message>");
// Clear OOME if present.
(*env)->ExceptionClear(env);
}
(*env)->DeleteLocalRef(env, message);
message = NULL;
return success;
}
static jobject NewStringWriter(JNIEnv* env) {
jclass clazz = (*env)->FindClass(env, "java/io/StringWriter");
jmethodID init = (*env)->GetMethodID(env, clazz, "<init>", "()V");
jobject instance = (*env)->NewObject(env, clazz, init);
(*env)->DeleteLocalRef(env, clazz);
return instance;
}
static jstring StringWriterToString(JNIEnv* env, jobject stringWriter) {
jmethodID toString =
FindMethod(env, "java/io/StringWriter", "toString", "()Ljava/lang/String;");
return (jstring) (*env)->CallObjectMethod(env, stringWriter, toString);
}
static jobject NewPrintWriter(JNIEnv* env, jobject writer) {
jclass clazz = (*env)->FindClass(env, "java/io/PrintWriter");
jmethodID init = (*env)->GetMethodID(env, clazz, "<init>", "(Ljava/io/Writer;)V");
jobject instance = (*env)->NewObject(env, clazz, init, writer);
(*env)->DeleteLocalRef(env, clazz);
return instance;
}
static bool GetStackTrace(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
// This function is equivalent to the following Java snippet:
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// thrown.printStackTrace(pw);
// String trace = sw.toString();
// return trace;
jobject sw = NewStringWriter(env);
if (sw == NULL) {
return false;
}
jobject pw = NewPrintWriter(env, sw);
if (pw == NULL) {
(*env)->DeleteLocalRef(env, sw);
return false;
}
jmethodID printStackTrace =
FindMethod(env, "java/lang/Throwable", "printStackTrace", "(Ljava/io/PrintWriter;)V");
(*env)->CallVoidMethod(env, thrown, printStackTrace, pw);
jstring trace = StringWriterToString(env, sw);
(*env)->DeleteLocalRef(env, pw);
pw = NULL;
(*env)->DeleteLocalRef(env, sw);
sw = NULL;
if (trace == NULL) {
return false;
}
bool success = AppendJString(env, trace, dst);
(*env)->DeleteLocalRef(env, trace);
return success;
}
static void GetStackTraceOrSummary(JNIEnv* env, jthrowable thrown, struct ExpandableString* dst) {
// This method attempts to get a stack trace or summary info for an exception.
// The exception may be provided in the |thrown| argument to this function.
// If |thrown| is NULL, then any pending exception is used if it exists.
// Save pending exception, callees may raise other exceptions. Any pending exception is
// rethrown when this function exits.
jthrowable pendingException = (*env)->ExceptionOccurred(env);
if (pendingException != NULL) {
(*env)->ExceptionClear(env);
}
if (thrown == NULL) {
if (pendingException == NULL) {
ExpandableStringAssign(dst, "<no pending exception>");
return;
}
thrown = pendingException;
}
if (!GetStackTrace(env, thrown, dst)) {
// GetStackTrace may have raised an exception, clear it since it's not for the caller.
(*env)->ExceptionClear(env);
GetExceptionSummary(env, thrown, dst);
}
if (pendingException != NULL) {
// Re-throw the pending exception present when this method was called.
(*env)->Throw(env, pendingException);
(*env)->DeleteLocalRef(env, pendingException);
}
}
static void DiscardPendingException(JNIEnv* env, const char* className) {
jthrowable exception = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env);
if (exception == NULL) {
return;
}
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
GetExceptionSummary(env, exception, &summary);
const char* details = (summary.data != NULL) ? summary.data : "Unknown";
ALOGW("Discarding pending exception (%s) to throw %s", details, className);
ExpandableStringRelease(&summary);
(*env)->DeleteLocalRef(env, exception);
}
static int ThrowException(JNIEnv* env, const char* className, const char* ctorSig, ...) {
int status = -1;
jclass exceptionClass = NULL;
va_list args;
va_start(args, ctorSig);
DiscardPendingException(env, className);
{
/* We want to clean up local references before returning from this function, so,
* regardless of return status, the end block must run. Have the work done in a
* nested block to avoid using any uninitialized variables in the end block. */
exceptionClass = (*env)->FindClass(env, className);
if (exceptionClass == NULL) {
ALOGE("Unable to find exception class %s", className);
/* an exception, most likely ClassNotFoundException, will now be pending */
goto end;
}
jmethodID init = (*env)->GetMethodID(env, exceptionClass, "<init>", ctorSig);
if(init == NULL) {
ALOGE("Failed to find constructor for '%s' '%s'", className, ctorSig);
goto end;
}
jobject instance = (*env)->NewObjectV(env, exceptionClass, init, args);
if (instance == NULL) {
ALOGE("Failed to construct '%s'", className);
goto end;
}
if ((*env)->Throw(env, (jthrowable)instance) != JNI_OK) {
ALOGE("Failed to throw '%s'", className);
/* an exception, most likely OOM, will now be pending */
goto end;
}
/* everything worked fine, just update status to success and clean up */
status = 0;
}
end:
va_end(args);
if (exceptionClass != NULL) {
(*env)->DeleteLocalRef(env, exceptionClass);
}
return status;
}
static jstring CreateExceptionMsg(JNIEnv* env, const char* msg) {
jstring detailMessage = (*env)->NewStringUTF(env, msg);
if (detailMessage == NULL) {
/* Not really much we can do here. We're probably dead in the water,
but let's try to stumble on... */
(*env)->ExceptionClear(env);
}
return detailMessage;
}
/* Helper macro to deal with conversion of the exception message from a C string
* to jstring.
*
* This is useful because most exceptions have a message as the first parameter
* and delegating the conversion to all the callers of ThrowException results in
* code duplication. However, since we try to allow variable number of arguments
* for the exception constructor we'd either need to do the conversion inside
* the macro, or manipulate the va_list to replace the C string to a jstring.
* This seems like the cleaner solution.
*/
#define THROW_EXCEPTION_WITH_MESSAGE(env, className, ctorSig, msg, ...) ({ \
jstring _detailMessage = CreateExceptionMsg(env, msg); \
int _status = ThrowException(env, className, ctorSig, _detailMessage, ## __VA_ARGS__); \
if (_detailMessage != NULL) { \
(*env)->DeleteLocalRef(env, _detailMessage); \
} \
_status; })
//
// JNIHelp external API
//
int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* methods, int numMethods)
{
ALOGV("Registering %s's %d native methods...", className, numMethods);
jclass clazz = (*env)->FindClass(env, className);
ALOG_ALWAYS_FATAL_IF(clazz == NULL,
"Native registration unable to find class '%s'; aborting...",
className);
int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);
(*env)->DeleteLocalRef(env, clazz);
if (result == 0) {
return 0;
}
// Failure to register natives is fatal. Try to report the corresponding exception,
// otherwise abort with generic failure message.
jthrowable thrown = (*env)->ExceptionOccurred(env);
if (thrown != NULL) {
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
if (GetExceptionSummary(env, thrown, &summary)) {
ALOGF("%s", summary.data);
}
ExpandableStringRelease(&summary);
(*env)->DeleteLocalRef(env, thrown);
}
ALOGF("RegisterNatives failed for '%s'; aborting...", className);
return result;
}
void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable thrown) {
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
GetStackTraceOrSummary(env, thrown, &summary);
const char* details = (summary.data != NULL) ? summary.data : "No memory to report exception";
__android_log_write(priority, tag, details);
ExpandableStringRelease(&summary);
}
int jniThrowException(JNIEnv* env, const char* className, const char* message) {
return THROW_EXCEPTION_WITH_MESSAGE(env, className, "(Ljava/lang/String;)V", message);
}
int jniThrowExceptionFmt(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(JNIEnv* env, const char* msg) {
return jniThrowException(env, "java/lang/NullPointerException", msg);
}
int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
return jniThrowException(env, "java/lang/RuntimeException", msg);
}
int jniThrowIOException(JNIEnv* env, int errno_value) {
char buffer[80];
const char* message = platformStrError(errno_value, buffer, sizeof(buffer));
return jniThrowException(env, "java/io/IOException", message);
}
int jniThrowErrnoException(JNIEnv* env, const char* functionName, int errno_value) {
return THROW_EXCEPTION_WITH_MESSAGE(env, "android/system/ErrnoException",
"(Ljava/lang/String;I)V", functionName, errno_value);
}
jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
return (*env)->NewString(env, unicodeChars, len);
}

65
JNIPlatformHelp.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2006 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 "include_platform/nativehelper/JNIPlatformHelp.h"
#include <stddef.h>
#include "JniConstants.h"
static int GetBufferPosition(JNIEnv* env, jobject nioBuffer) {
return(*env)->GetIntField(env, nioBuffer, JniConstants_NioBuffer_position(env));
}
static int GetBufferLimit(JNIEnv* env, jobject nioBuffer) {
return(*env)->GetIntField(env, nioBuffer, JniConstants_NioBuffer_limit(env));
}
static int GetBufferElementSizeShift(JNIEnv* env, jobject nioBuffer) {
return(*env)->GetIntField(env, nioBuffer, JniConstants_NioBuffer__elementSizeShift(env));
}
jarray jniGetNioBufferBaseArray(JNIEnv* env, jobject nioBuffer) {
jclass nioAccessClass = JniConstants_NIOAccessClass(env);
jmethodID getBaseArrayMethod = JniConstants_NIOAccess_getBaseArray(env);
jobject object = (*env)->CallStaticObjectMethod(env,
nioAccessClass, getBaseArrayMethod, nioBuffer);
return (jarray) object;
}
int jniGetNioBufferBaseArrayOffset(JNIEnv* env, jobject nioBuffer) {
jclass nioAccessClass = JniConstants_NIOAccessClass(env);
jmethodID getBaseArrayOffsetMethod = JniConstants_NIOAccess_getBaseArrayOffset(env);
return (*env)->CallStaticIntMethod(env, nioAccessClass, getBaseArrayOffsetMethod, nioBuffer);
}
jlong jniGetNioBufferPointer(JNIEnv* env, jobject nioBuffer) {
jlong baseAddress = (*env)->GetLongField(env, nioBuffer, JniConstants_NioBuffer_address(env));
if (baseAddress != 0) {
const int position = GetBufferPosition(env, nioBuffer);
const int shift = GetBufferElementSizeShift(env, nioBuffer);
baseAddress += position << shift;
}
return baseAddress;
}
jlong jniGetNioBufferFields(JNIEnv* env, jobject nioBuffer,
jint* position, jint* limit, jint* elementSizeShift) {
*position = GetBufferPosition(env, nioBuffer);
*limit = GetBufferLimit(env, nioBuffer);
*elementSizeShift = GetBufferElementSizeShift(env, nioBuffer);
return (*env)->GetLongField(env, nioBuffer, JniConstants_NioBuffer_address(env));
}

195
JniConstants.c Normal file
View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2010 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 "JniConstants.h"
#include <pthread.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#define LOG_TAG "JniConstants"
#include "ALog-priv.h"
// jclass constants list:
// <class, signature, androidOnly>
#define JCLASS_CONSTANTS_LIST(V) \
V(FileDescriptor, "java/io/FileDescriptor", false) \
V(NIOAccess, "java/nio/NIOAccess", true) \
V(NioBuffer, "java/nio/Buffer", false)
// jmethodID's of public methods constants list:
// <Class, method, method-string, signature, is_static>
#define JMETHODID_CONSTANTS_LIST(V) \
V(FileDescriptor, init, "<init>", "()V", false) \
V(NIOAccess, getBaseArray, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;", true) \
V(NIOAccess, getBaseArrayOffset, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I", true) \
V(NioBuffer, array, "array", "()Ljava/lang/Object;", false) \
V(NioBuffer, arrayOffset, "arrayOffset", "()I", false)
// jfieldID constants list:
// <Class, field, signature, is_static>
#define JFIELDID_CONSTANTS_LIST(V) \
V(FileDescriptor, descriptor, "I", false) \
V(NioBuffer, _elementSizeShift, "I", false) \
V(NioBuffer, address, "J", false) \
V(NioBuffer, limit, "I", false) \
V(NioBuffer, position, "I", false)
#define CLASS_NAME(cls) g_ ## cls
#define METHOD_NAME(cls, method) g_ ## cls ## _ ## method
#define FIELD_NAME(cls, field) g_ ## cls ## _ ## field
//
// Declare storage for cached classes, methods and fields.
//
#define JCLASS_DECLARE_STORAGE(cls, ...) \
static jclass CLASS_NAME(cls) = NULL;
JCLASS_CONSTANTS_LIST(JCLASS_DECLARE_STORAGE)
#undef JCLASS_DECLARE_STORAGE
#define JMETHODID_DECLARE_STORAGE(cls, method, ...) \
static jmethodID METHOD_NAME(cls, method) = NULL;
JMETHODID_CONSTANTS_LIST(JMETHODID_DECLARE_STORAGE)
#undef JMETHODID_DECLARE_STORAGE
#define JFIELDID_DECLARE_STORAGE(cls, field, ...) \
static jfieldID FIELD_NAME(cls, field) = NULL;
JFIELDID_CONSTANTS_LIST(JFIELDID_DECLARE_STORAGE)
#undef JFIELDID_DECLARE_STORAGE
//
// Helper methods
//
static jclass FindClass(JNIEnv* env, const char* signature, bool androidOnly) {
jclass cls = (*env)->FindClass(env, signature);
if (cls == NULL) {
ALOG_ALWAYS_FATAL_IF(!androidOnly, "Class not found: %s", signature);
return NULL;
}
return (*env)->NewGlobalRef(env, cls);
}
static jmethodID FindMethod(JNIEnv* env, jclass cls,
const char* name, const char* signature, bool isStatic) {
jmethodID method;
if (isStatic) {
method = (*env)->GetStaticMethodID(env, cls, name, signature);
} else {
method = (*env)->GetMethodID(env, cls, name, signature);
}
ALOG_ALWAYS_FATAL_IF(method == NULL, "Method not found: %s:%s", name, signature);
return method;
}
static jfieldID FindField(JNIEnv* env, jclass cls,
const char* name, const char* signature, bool isStatic) {
jfieldID field;
if (isStatic) {
field = (*env)->GetStaticFieldID(env, cls, name, signature);
} else {
field = (*env)->GetFieldID(env, cls, name, signature);
}
ALOG_ALWAYS_FATAL_IF(field == NULL, "Field not found: %s:%s", name, signature);
return field;
}
static pthread_once_t g_initialized = PTHREAD_ONCE_INIT;
static JNIEnv* g_init_env;
static void InitializeConstants() {
// Initialize cached classes.
#define JCLASS_INITIALIZE(cls, signature, androidOnly) \
CLASS_NAME(cls) = FindClass(g_init_env, signature, androidOnly);
JCLASS_CONSTANTS_LIST(JCLASS_INITIALIZE)
#undef JCLASS_INITIALIZE
// Initialize cached methods.
#define JMETHODID_INITIALIZE(cls, method, name, signature, isStatic) \
METHOD_NAME(cls, method) = \
FindMethod(g_init_env, CLASS_NAME(cls), name, signature, isStatic);
JMETHODID_CONSTANTS_LIST(JMETHODID_INITIALIZE)
#undef JMETHODID_INITIALIZE
// Initialize cached fields.
#define JFIELDID_INITIALIZE(cls, field, signature, isStatic) \
FIELD_NAME(cls, field) = \
FindField(g_init_env, CLASS_NAME(cls), #field, signature, isStatic);
JFIELDID_CONSTANTS_LIST(JFIELDID_INITIALIZE)
#undef JFIELDID_INITIALIZE
}
void EnsureInitialized(JNIEnv* env) {
// This method has to be called in every cache accesses because library can be built
// 2 different ways and existing usage for compat version doesn't have a good hook for
// initialization and is widely used.
g_init_env = env;
pthread_once(&g_initialized, InitializeConstants);
}
// API exported by libnativehelper_api.h.
void jniUninitializeConstants() {
// Uninitialize cached classes, methods and fields.
//
// NB we assume the runtime is stopped at this point and do not delete global
// references.
#define JCLASS_INVALIDATE(cls, ...) CLASS_NAME(cls) = NULL;
JCLASS_CONSTANTS_LIST(JCLASS_INVALIDATE);
#undef JCLASS_INVALIDATE
#define JMETHODID_INVALIDATE(cls, method, ...) METHOD_NAME(cls, method) = NULL;
JMETHODID_CONSTANTS_LIST(JMETHODID_INVALIDATE);
#undef JMETHODID_INVALIDATE
#define JFIELDID_INVALIDATE(cls, field, ...) FIELD_NAME(cls, field) = NULL;
JFIELDID_CONSTANTS_LIST(JFIELDID_INVALIDATE);
#undef JFIELDID_INVALIDATE
// If jniConstantsUninitialize is called, runtime has shutdown. Reset
// state as some tests re-start the runtime.
pthread_once_t o = PTHREAD_ONCE_INIT;
memcpy(&g_initialized, &o, sizeof(o));
}
//
// Accessors
//
#define JCLASS_ACCESSOR_IMPL(cls, ...) \
jclass JniConstants_ ## cls ## Class(JNIEnv* env) { \
EnsureInitialized(env); \
return CLASS_NAME(cls); \
}
JCLASS_CONSTANTS_LIST(JCLASS_ACCESSOR_IMPL)
#undef JCLASS_ACCESSOR_IMPL
#define JMETHODID_ACCESSOR_IMPL(cls, method, ...) \
jmethodID JniConstants_ ## cls ## _ ## method(JNIEnv* env) { \
EnsureInitialized(env); \
return METHOD_NAME(cls, method); \
}
JMETHODID_CONSTANTS_LIST(JMETHODID_ACCESSOR_IMPL)
#define JFIELDID_ACCESSOR_IMPL(cls, field, ...) \
jfieldID JniConstants_ ## cls ## _ ## field(JNIEnv* env) { \
EnsureInitialized(env); \
return FIELD_NAME(cls, field); \
}
JFIELDID_CONSTANTS_LIST(JFIELDID_ACCESSOR_IMPL)

59
JniConstants.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright 2018 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
#include <sys/cdefs.h>
#include <jni.h>
__BEGIN_DECLS
//
// Classes in constants cache.
//
// NB The implementations of these methods are generated by the JCLASS_ACCESSOR_IMPL macro in
// JniConstants.c.
//
jclass JniConstants_FileDescriptorClass(JNIEnv* env);
jclass JniConstants_NIOAccessClass(JNIEnv* env);
jclass JniConstants_NioBufferClass(JNIEnv* env);
//
// Methods in the constants cache.
//
// NB The implementations of these methods are generated by the JMETHODID_ACCESSOR_IMPL macro in
// JniConstants.c.
//
jmethodID JniConstants_FileDescriptor_init(JNIEnv* env);
jmethodID JniConstants_NIOAccess_getBaseArray(JNIEnv* env);
jmethodID JniConstants_NIOAccess_getBaseArrayOffset(JNIEnv* env);
jmethodID JniConstants_NioBuffer_array(JNIEnv* env);
jmethodID JniConstants_NioBuffer_arrayOffset(JNIEnv* env);
//
// Fields in the constants cache.
//
// NB The implementations of these methods are generated by the JFIELDID_ACCESSOR_IMPL macro in
// JniConstants.c.
//
jfieldID JniConstants_FileDescriptor_descriptor(JNIEnv* env);
jfieldID JniConstants_NioBuffer_address(JNIEnv* env);
jfieldID JniConstants_NioBuffer__elementSizeShift(JNIEnv* env);
jfieldID JniConstants_NioBuffer_limit(JNIEnv* env);
jfieldID JniConstants_NioBuffer_position(JNIEnv* env);
__END_DECLS

28
JniInvocation-priv.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 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
#include <sys/cdefs.h>
__BEGIN_DECLS
const char* JniInvocationGetLibraryWith(const char* library,
bool is_debuggable,
const char* system_preferred_library);
__END_DECLS

224
JniInvocation.c Normal file
View File

@ -0,0 +1,224 @@
/*
* Copyright (C) 2013 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 "include_platform/nativehelper/JniInvocation.h"
#define LOG_TAG "JniInvocation"
#include "ALog-priv.h"
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include <errno.h>
#include <jni.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "DlHelp.h"
// Name the default library providing the JNI Invocation API.
static const char* kDefaultJniInvocationLibrary = "libart.so";
static const char* kDebugJniInvocationLibrary = "libartd.so";
#if defined(__LP64__)
#define LIB_DIR "lib64"
#else
#define LIB_DIR "lib"
#endif
static const char* kDebugJniInvocationLibraryPath = "/apex/com.android.art/" LIB_DIR "/libartd.so";
struct JniInvocationImpl {
// Name of library providing JNI_ method implementations.
const char* jni_provider_library_name;
// Opaque pointer to shared library from dlopen / LoadLibrary.
void* jni_provider_library;
// Function pointers to methods in JNI provider.
jint (*JNI_GetDefaultJavaVMInitArgs)(void*);
jint (*JNI_CreateJavaVM)(JavaVM**, JNIEnv**, void*);
jint (*JNI_GetCreatedJavaVMs)(JavaVM**, jsize, jsize*);
};
static struct JniInvocationImpl g_impl;
//
// Internal helpers.
//
#define UNUSED(x) (x) = (x)
static bool IsDebuggable() {
#ifdef __ANDROID__
char debuggable[PROP_VALUE_MAX] = {0};
__system_property_get("ro.debuggable", debuggable);
return strcmp(debuggable, "1") == 0;
#else
// Host is always treated as debuggable, which allows choice of library to be overridden.
return true;
#endif
}
static int GetLibrarySystemProperty(char* buffer) {
#ifdef __ANDROID__
return __system_property_get("persist.sys.dalvik.vm.lib.2", buffer);
#else
// Host does not use properties.
UNUSED(buffer);
return 0;
#endif
}
static DlSymbol FindSymbol(DlLibrary library, const char* symbol) {
DlSymbol s = DlGetSymbol(library, symbol);
if (s == NULL) {
ALOGE("Failed to find symbol: %s", symbol);
}
return s;
}
//
// Exported functions for JNI based VM management from JNI spec.
//
jint JNI_GetDefaultJavaVMInitArgs(void* vmargs) {
ALOG_ALWAYS_FATAL_IF(NULL == g_impl.JNI_GetDefaultJavaVMInitArgs, "Runtime library not loaded.");
return g_impl.JNI_GetDefaultJavaVMInitArgs(vmargs);
}
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
ALOG_ALWAYS_FATAL_IF(NULL == g_impl.JNI_CreateJavaVM, "Runtime library not loaded.");
return g_impl.JNI_CreateJavaVM(p_vm, p_env, vm_args);
}
jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
if (NULL == g_impl.JNI_GetCreatedJavaVMs) {
*vm_count = 0;
return JNI_OK;
}
return g_impl.JNI_GetCreatedJavaVMs(vms, size, vm_count);
}
//
// JniInvocation functions for setting up JNI functions.
//
const char* JniInvocationGetLibraryWith(const char* library,
bool is_debuggable,
const char* system_preferred_library) {
if (is_debuggable) {
// Debuggable property is set. Allow library providing JNI Invocation API to be overridden.
// Choose the library parameter (if provided).
if (library != NULL) {
return library;
}
// If the debug library is installed, use it.
// TODO(b/216099383): Do this in the test harness instead.
struct stat st;
if (stat(kDebugJniInvocationLibraryPath, &st) == 0) {
return kDebugJniInvocationLibrary;
} else if (errno != ENOENT) {
ALOGW("Failed to stat %s: %s", kDebugJniInvocationLibraryPath, strerror(errno));
}
// Choose the system_preferred_library (if provided).
if (system_preferred_library != NULL) {
return system_preferred_library;
}
}
return kDefaultJniInvocationLibrary;
}
const char* JniInvocationGetLibrary(const char* library, char* buffer) {
bool debuggable = IsDebuggable();
const char* system_preferred_library = NULL;
if (buffer != NULL && (GetLibrarySystemProperty(buffer) > 0)) {
system_preferred_library = buffer;
}
return JniInvocationGetLibraryWith(library, debuggable, system_preferred_library);
}
struct JniInvocationImpl* JniInvocationCreate() {
// Android only supports a single JniInvocation instance and only a single JavaVM.
if (g_impl.jni_provider_library != NULL) {
return NULL;
}
return &g_impl;
}
bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library_name) {
#ifdef __ANDROID__
char buffer[PROP_VALUE_MAX];
#else
char* buffer = NULL;
#endif
library_name = JniInvocationGetLibrary(library_name, buffer);
DlLibrary library = DlOpenLibrary(library_name);
if (library == NULL) {
if (strcmp(library_name, kDefaultJniInvocationLibrary) == 0) {
// Nothing else to try.
ALOGE("Failed to dlopen %s: %s", library_name, DlGetError());
return false;
}
// Note that this is enough to get something like the zygote
// running, we can't property_set here to fix this for the future
// because we are root and not the system user. See
// RuntimeInit.commonInit for where we fix up the property to
// avoid future fallbacks. http://b/11463182
ALOGW("Falling back from %s to %s after dlopen error: %s",
library_name, kDefaultJniInvocationLibrary, DlGetError());
library_name = kDefaultJniInvocationLibrary;
library = DlOpenLibrary(library_name);
if (library == NULL) {
ALOGE("Failed to dlopen %s: %s", library_name, DlGetError());
return false;
}
}
DlSymbol JNI_GetDefaultJavaVMInitArgs_ = FindSymbol(library, "JNI_GetDefaultJavaVMInitArgs");
if (JNI_GetDefaultJavaVMInitArgs_ == NULL) {
return false;
}
DlSymbol JNI_CreateJavaVM_ = FindSymbol(library, "JNI_CreateJavaVM");
if (JNI_CreateJavaVM_ == NULL) {
return false;
}
DlSymbol JNI_GetCreatedJavaVMs_ = FindSymbol(library, "JNI_GetCreatedJavaVMs");
if (JNI_GetCreatedJavaVMs_ == NULL) {
return false;
}
instance->jni_provider_library_name = library_name;
instance->jni_provider_library = library;
instance->JNI_GetDefaultJavaVMInitArgs = (jint (*)(void *)) JNI_GetDefaultJavaVMInitArgs_;
instance->JNI_CreateJavaVM = (jint (*)(JavaVM**, JNIEnv**, void*)) JNI_CreateJavaVM_;
instance->JNI_GetCreatedJavaVMs = (jint (*)(JavaVM**, jsize, jsize*)) JNI_GetCreatedJavaVMs_;
return true;
}
void JniInvocationDestroy(struct JniInvocationImpl* instance) {
DlCloseLibrary(instance->jni_provider_library);
memset(instance, 0, sizeof(*instance));
}

190
NOTICE Normal file
View File

@ -0,0 +1,190 @@
Copyright (c) 2005-2008, 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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

3
OWNERS Normal file
View File

@ -0,0 +1,3 @@
mast@google.com
ngeoffray@google.com
oth@google.com

2
PREUPLOAD.cfg Normal file
View File

@ -0,0 +1,2 @@
[Builtin Hooks]
bpfmt = true

97
README.md Normal file
View File

@ -0,0 +1,97 @@
# libnativehelper
libnativehelper is a collection of JNI related utilities used in Android.
There are several header and binary libraries here and not all of the
functionality fits together well. The header libraries are mostly C++
based. The binary libraries are entirely written in C with no C++
dependencies. This is by design as the code here can be distributed in
multiple ways, including mainline modules, so keeping the size down
benefits everyone with smaller downloads and a stable ABI.
## Header Libraries
### jni_headers
This is a header library that contains provides the API represented in
the JNI Specification 1.6. Any project in Android that depends on
`jni.h` should depend on this.
See:
* [jni.h](include_jni/jni.h)
### libnativehelper_header_only
These headers provide utilities that defined entirely within the
headers. There are scoped resource classes that make common JNI
patterns of acquiring and releasing resources safer to use than the
JNI specification equivalents. Examples being `ScopedLocalRef` to
manage the lifetime of local references and `ScopedUtfChars` to manage
the lifetime of Java strings in native code and provide access to utf8
characters.
See:
* [nativehelper/nativehelper_utils.h](header_only_include/nativehelper/nativehelper_utils.h)
* [nativehelper/scoped_utf_chars.h](header_only_include/nativehelper/scoped_utf_chars.h)
* [nativehelper/scoped_bytes.h](header_only_include/nativehelper/scoped_bytes.h)
* [nativehelper/scoped_string_chars.h](header_only_include/nativehelper/scoped_string_chars.h)
* [nativehelper/scoped_primitive_array.h](header_only_include/nativehelper/scoped_primitive_array.h)
* [nativehelper/scoped_local_ref.h](header_only_include/nativehelper/scoped_local_ref.h)
* [nativehelper/scoped_local_frame.h](header_only_include/nativehelper/scoped_local_frame.h)
### jni_platform_headers
The `jni_macros.h` header provide compile time checking of JNI methods
implemented in C++. They ensure the C++ method declaration match the
Java signature they are associated with.
See:
* [nativehelper/jni_macros.h](include_platform_header_only/nativehelper/jni_macros.h)
## Libraries
### libnativehelper
A shared library distributed in the ART module that provides helper
routines built on Java APIs. This library depends on details that are
private to libcore and use should be restricted to platform code and
within the ART module.
This library also contains the JNI invocation API from the JNI
Specification and the glue that connects the ART runtime to the API
implementation. The glue logic is platform only as it is used with the
Zygote and the standalone dalvikvm.
See:
* [nativehelper/JNIHelp.h](include/nativehelper/JNIHelp.h)
* [nativehelper/JniInvocation.h](include_platform/nativehelper/JniInvocation.h)
* [nativehelper/JNIPlatformHelp.h](include_platform/nativehelper/JNIPlatformHelp.h)
* [nativehelper/ScopedBytes.h](include/nativehelper/ScopedBytes.h)
* [nativehelper/ScopedUtfChars.h](include/nativehelper/ScopedUtfChars.h)
* [nativehelper/ScopedLocalFrame.h](include/nativehelper/ScopedLocalFrame.h)
* [nativehelper/ScopedLocalRef.h](include/nativehelper/ScopedLocalRef.h)
* [nativehelper/ScopedPrimitiveArray.h](include/nativehelper/ScopedPrimitiveArray.h)
* [nativehelper/ScopedStringChars.h](include/nativehelper/ScopedStringChars.h)
* [nativehelper/toStringArray.h](include/nativehelper/toStringArray.h)
### libnativehelper_compat_libc++
This shared and static library contains a subset of the helper
routines in libnativehelper based on public Java API. This code can be
statically linked as the Java APIs it depends on are considered
stable. The name of this library is a misnomer since it contains no
C++ code.
See:
* [nativehelper/JNIHelp.h](include/nativehelper/JNIHelp.h)
* [nativehelper/ScopedBytes.h](include/nativehelper/ScopedBytes.h)
* [nativehelper/ScopedUtfChars.h](include/nativehelper/ScopedUtfChars.h)
* [nativehelper/ScopedLocalFrame.h](include/nativehelper/ScopedLocalFrame.h)
* [nativehelper/ScopedLocalRef.h](include/nativehelper/ScopedLocalRef.h)
* [nativehelper/ScopedPrimitiveArray.h](include/nativehelper/ScopedPrimitiveArray.h)
* [nativehelper/ScopedStringChars.h](include/nativehelper/ScopedStringChars.h)
* [nativehelper/toStringArray.h](include/nativehelper/toStringArray.h)

19
TEST_MAPPING Normal file
View File

@ -0,0 +1,19 @@
{
"presubmit": [
{
"name": "libnativehelper_tests"
},
{
"name": "libnativehelper_lazy_tests"
},
{
"name": "MtsLibnativehelperTestCases"
},
{
"name": "MtsLibnativehelperLazyTestCases"
},
{
"name": "CtsLibnativehelperTestCases"
}
]
}

47
file_descriptor_jni.c Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2020 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 <android/file_descriptor_jni.h>
#include <stddef.h>
#define LOG_TAG "file_descriptor_jni"
#include "ALog-priv.h"
#include "JniConstants.h"
static void EnsureArgumentIsFileDescriptor(JNIEnv* env, jobject instance) {
ALOG_ALWAYS_FATAL_IF(instance == NULL, "FileDescriptor is NULL");
jclass jifd = JniConstants_FileDescriptorClass(env);
ALOG_ALWAYS_FATAL_IF(!(*env)->IsInstanceOf(env, instance, jifd),
"Argument is not a FileDescriptor");
}
JNIEXPORT _Nullable jobject AFileDescriptor_create(JNIEnv* env) {
return (*env)->NewObject(env,
JniConstants_FileDescriptorClass(env),
JniConstants_FileDescriptor_init(env));
}
JNIEXPORT int AFileDescriptor_getFd(JNIEnv* env, jobject fileDescriptor) {
EnsureArgumentIsFileDescriptor(env, fileDescriptor);
return (*env)->GetIntField(env, fileDescriptor, JniConstants_FileDescriptor_descriptor(env));
}
JNIEXPORT void AFileDescriptor_setFd(JNIEnv* env, jobject fileDescriptor, int fd) {
EnsureArgumentIsFileDescriptor(env, fileDescriptor);
(*env)->SetIntField(env, fileDescriptor, JniConstants_FileDescriptor_descriptor(env), fd);
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2007 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
#include <jni.h>
#if defined(__cplusplus)
#if !defined(DISALLOW_COPY_AND_ASSIGN)
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
// declarations in a class.
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
void operator=(const TypeName&) = delete
#endif // !defined(DISALLOW_COPY_AND_ASSIGN)
// This seems a header-only include. Provide NPE throwing.
static inline int jniThrowNullPointerException(JNIEnv* env) {
if (env->ExceptionCheck()) {
// Drop any pending exception.
env->ExceptionClear();
}
jclass e_class = env->FindClass("java/lang/NullPointerException");
if (e_class == nullptr) {
return -1;
}
if (env->ThrowNew(e_class, nullptr) != JNI_OK) {
env->DeleteLocalRef(e_class);
return -1;
}
env->DeleteLocalRef(e_class);
return 0;
}
#endif // defined(__cplusplus)

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2010 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
#include <jni.h>
#include "nativehelper_utils.h"
class ScopedLocalFrame {
public:
explicit ScopedLocalFrame(JNIEnv* env) : mEnv(env) {
mEnv->PushLocalFrame(128);
}
~ScopedLocalFrame() {
mEnv->PopLocalFrame(nullptr);
}
private:
JNIEnv* const mEnv;
DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame);
};

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2010 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
#include <cstddef>
#include <jni.h>
#include "nativehelper_utils.h"
// A smart pointer that deletes a JNI local reference when it goes out of scope.
template<typename T>
class ScopedLocalRef {
public:
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
}
ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) {
}
explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) {
}
~ScopedLocalRef() {
reset();
}
void reset(T ptr = nullptr) {
if (ptr != mLocalRef) {
if (mLocalRef != nullptr) {
mEnv->DeleteLocalRef(mLocalRef);
}
mLocalRef = ptr;
}
}
T release() __attribute__((warn_unused_result)) {
T localRef = mLocalRef;
mLocalRef = nullptr;
return localRef;
}
T get() const {
return mLocalRef;
}
// We do not expose an empty constructor as it can easily lead to errors
// using common idioms, e.g.:
// ScopedLocalRef<...> ref;
// ref.reset(...);
// Move assignment operator.
ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept {
reset(s.release());
mEnv = s.mEnv;
return *this;
}
// Allows "if (scoped_ref == nullptr)"
bool operator==(std::nullptr_t) const {
return mLocalRef == nullptr;
}
// Allows "if (scoped_ref != nullptr)"
bool operator!=(std::nullptr_t) const {
return mLocalRef != nullptr;
}
private:
JNIEnv* mEnv;
T mLocalRef;
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
};

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2010 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
#include <stddef.h>
#include <jni.h>
#include "nativehelper_utils.h"
#ifdef POINTER_TYPE
#error POINTER_TYPE is defined.
#else
#define POINTER_TYPE(T) T* /* NOLINT */
#endif
#ifdef REFERENCE_TYPE
#error REFERENCE_TYPE is defined.
#else
#define REFERENCE_TYPE(T) T& /* NOLINT */
#endif
// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
// access and should be used by default.
#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
class Scoped ## NAME ## ArrayRO { \
public: \
explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
: mEnv(env), mJavaArray(nullptr), mRawArray(nullptr), mSize(0) {} \
Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
: mEnv(env) { \
if (javaArray == nullptr) { \
mJavaArray = nullptr; \
mSize = 0; \
mRawArray = nullptr; \
jniThrowNullPointerException(mEnv); \
} else { \
reset(javaArray); \
} \
} \
~Scoped ## NAME ## ArrayRO() { \
if (mRawArray != nullptr && mRawArray != mBuffer) { \
mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
} \
} \
void reset(PRIMITIVE_TYPE ## Array javaArray) { \
mJavaArray = javaArray; \
mSize = mEnv->GetArrayLength(mJavaArray); \
if (mSize <= buffer_size) { \
mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \
mRawArray = mBuffer; \
} else { \
mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
} \
} \
const PRIMITIVE_TYPE* get() const { return mRawArray; } \
PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
size_t size() const { return mSize; } \
private: \
static const jsize buffer_size = 1024; \
JNIEnv* const mEnv; \
PRIMITIVE_TYPE ## Array mJavaArray; \
POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
jsize mSize; \
PRIMITIVE_TYPE mBuffer[buffer_size]; \
DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \
}
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
// convenient read-write access to Java arrays from JNI code. These are more expensive,
// since they entail a copy back onto the Java heap, and should only be used when necessary.
#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
class Scoped ## NAME ## ArrayRW { \
public: \
explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
: mEnv(env), mJavaArray(nullptr), mRawArray(nullptr) {} \
Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
: mEnv(env), mJavaArray(javaArray), mRawArray(nullptr) { \
if (mJavaArray == nullptr) { \
jniThrowNullPointerException(mEnv); \
} else { \
mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
} \
} \
~Scoped ## NAME ## ArrayRW() { \
if (mRawArray) { \
mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
} \
} \
void reset(PRIMITIVE_TYPE ## Array javaArray) { \
mJavaArray = javaArray; \
mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
} \
const PRIMITIVE_TYPE* get() const { return mRawArray; } \
PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; } \
REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \
size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
private: \
JNIEnv* const mEnv; \
PRIMITIVE_TYPE ## Array mJavaArray; \
POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \
}
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
#undef POINTER_TYPE
#undef REFERENCE_TYPE

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2011 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
#include <stddef.h>
#include <jni.h>
#include "nativehelper_utils.h"
// A smart pointer that provides access to a jchar* given a JNI jstring.
// Unlike GetStringChars, we throw NullPointerException rather than abort if
// passed a null jstring, and get will return nullptr.
// This makes the correct idiom very simple:
//
// ScopedStringChars name(env, java_name);
// if (name.get() == nullptr) {
// return nullptr;
// }
class ScopedStringChars {
public:
ScopedStringChars(JNIEnv* env, jstring s) : env_(env), string_(s), size_(0) {
if (s == nullptr) {
chars_ = nullptr;
jniThrowNullPointerException(env);
} else {
chars_ = env->GetStringChars(string_, nullptr);
if (chars_ != nullptr) {
size_ = env->GetStringLength(string_);
}
}
}
~ScopedStringChars() {
if (chars_ != nullptr) {
env_->ReleaseStringChars(string_, chars_);
}
}
const jchar* get() const {
return chars_;
}
size_t size() const {
return size_;
}
const jchar& operator[](size_t n) const {
return chars_[n];
}
private:
JNIEnv* const env_;
const jstring string_;
const jchar* chars_;
size_t size_;
DISALLOW_COPY_AND_ASSIGN(ScopedStringChars);
};

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2010 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
#include <stddef.h>
#include <string.h>
#include <jni.h>
#include "nativehelper_utils.h"
// A smart pointer that provides read-only access to a Java string's UTF chars.
// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
// passed a null jstring, and c_str will return nullptr.
// This makes the correct idiom very simple:
//
// ScopedUtfChars name(env, java_name);
// if (name.c_str() == nullptr) {
// return nullptr;
// }
class ScopedUtfChars {
public:
ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
if (s == nullptr) {
utf_chars_ = nullptr;
jniThrowNullPointerException(env);
} else {
utf_chars_ = env->GetStringUTFChars(s, nullptr);
}
}
ScopedUtfChars(ScopedUtfChars&& rhs) noexcept :
env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
rhs.env_ = nullptr;
rhs.string_ = nullptr;
rhs.utf_chars_ = nullptr;
}
~ScopedUtfChars() {
if (utf_chars_) {
env_->ReleaseStringUTFChars(string_, utf_chars_);
}
}
ScopedUtfChars& operator=(ScopedUtfChars&& rhs) noexcept {
if (this != &rhs) {
// Delete the currently owned UTF chars.
this->~ScopedUtfChars();
// Move the rhs ScopedUtfChars and zero it out.
env_ = rhs.env_;
string_ = rhs.string_;
utf_chars_ = rhs.utf_chars_;
rhs.env_ = nullptr;
rhs.string_ = nullptr;
rhs.utf_chars_ = nullptr;
}
return *this;
}
const char* c_str() const {
return utf_chars_;
}
size_t size() const {
return strlen(utf_chars_);
}
const char& operator[](size_t n) const {
return utf_chars_[n];
}
private:
JNIEnv* env_;
jstring string_;
const char* utf_chars_;
DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
};

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2020 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.
*/
/**
* @addtogroup FileDescriptor File Descriptor
* @{
*/
/**
* @file file_descriptor_jni.h
*/
#pragma once
#include <sys/cdefs.h>
#include <jni.h>
#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(x)
#endif
__BEGIN_DECLS
/**
* Returns a new java.io.FileDescriptor.
*
* The FileDescriptor created represents an invalid Unix file descriptor (represented by
* a file descriptor value of -1).
*
* Callers of this method should be aware that it can fail, returning NULL with a pending Java
* exception.
*
* Available since API level 31.
*
* \param env a pointer to the JNI Native Interface of the current thread.
* \return a java.io.FileDescriptor on success, nullptr if insufficient heap memory is available.
*/
jobject AFileDescriptor_create(JNIEnv* env) __INTRODUCED_IN(31);
/**
* Returns the Unix file descriptor represented by the given java.io.FileDescriptor.
*
* A return value of -1 indicates that \a fileDescriptor represents an invalid file descriptor.
*
* Aborts the program if \a fileDescriptor is not a java.io.FileDescriptor instance.
*
* Available since API level 31.
*
* \param env a pointer to the JNI Native Interface of the current thread.
* \param fileDescriptor a java.io.FileDescriptor instance.
* \return the Unix file descriptor wrapped by \a fileDescriptor.
*/
int AFileDescriptor_getFd(JNIEnv* env, jobject fileDescriptor) __INTRODUCED_IN(31);
/**
* Sets the Unix file descriptor represented by the given java.io.FileDescriptor.
*
* This function performs no validation of the Unix file descriptor argument, \a fd. Android uses
* the value -1 to represent an invalid file descriptor, all other values are considered valid.
* The validity of a file descriptor can be checked with FileDescriptor#valid().
*
* Aborts the program if \a fileDescriptor is not a java.io.FileDescriptor instance.
*
* Available since API level 31.
*
* \param env a pointer to the JNI Native Interface of the current thread.
* \param fileDescriptor a java.io.FileDescriptor instance.
* \param fd a Unix file descriptor that \a fileDescriptor will subsequently represent.
*/
void AFileDescriptor_setFd(JNIEnv* env, jobject fileDescriptor, int fd) __INTRODUCED_IN(31);
__END_DECLS
/** @} */

View File

@ -0,0 +1,511 @@
/*
* Copyright (C) 2007 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.
*/
/*
* JNI helper functions.
*
* This file may be included by C or C++ code, which is trouble because jni.h
* uses different typedefs for JNIEnv in each language.
*/
#pragma once
#include <sys/cdefs.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <jni.h>
#include <android/log.h>
// Avoid formatting this as it must match webview's usage (webview/graphics_utils.cpp).
// clang-format off
#ifndef NELEM
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
// clang-format on
/*
* For C++ code, we provide inlines that map to the C functions. g++ always
* inlines these, even on non-optimized builds.
*/
#if defined(__cplusplus)
namespace android::jnihelp {
struct [[maybe_unused]] ExpandableString {
size_t dataSize; // The length of the C string data (not including the null-terminator).
char* data; // The C string data.
};
[[maybe_unused]] static void ExpandableStringInitialize(struct ExpandableString* s) {
memset(s, 0, sizeof(*s));
}
[[maybe_unused]] static void ExpandableStringRelease(struct ExpandableString* s) {
free(s->data);
memset(s, 0, sizeof(*s));
}
[[maybe_unused]] static bool ExpandableStringAppend(struct ExpandableString* s, const char* text) {
size_t textSize = strlen(text);
size_t requiredSize = s->dataSize + textSize + 1;
char* data = (char*)realloc(s->data, requiredSize);
if (data == NULL) {
return false;
}
s->data = data;
memcpy(s->data + s->dataSize, text, textSize + 1);
s->dataSize += textSize;
return true;
}
[[maybe_unused]] static bool ExpandableStringAssign(struct ExpandableString* s, const char* text) {
ExpandableStringRelease(s);
return ExpandableStringAppend(s, text);
}
[[maybe_unused]] inline char* safe_strerror(char* (*strerror_r_method)(int, char*, size_t),
int errnum, char* buf, size_t buflen) {
return strerror_r_method(errnum, buf, buflen);
}
[[maybe_unused]] inline char* safe_strerror(int (*strerror_r_method)(int, char*, size_t),
int errnum, char* buf, size_t buflen) {
int rc = strerror_r_method(errnum, buf, buflen);
if (rc != 0) {
snprintf(buf, buflen, "errno %d", errnum);
}
return buf;
}
[[maybe_unused]] static const char* platformStrError(int errnum, char* buf, size_t buflen) {
return safe_strerror(strerror_r, errnum, buf, buflen);
}
[[maybe_unused]] static jmethodID FindMethod(JNIEnv* env, const char* className,
const char* methodName, const char* descriptor) {
// This method is only valid for classes in the core library which are
// not unloaded during the lifetime of managed code execution.
jclass clazz = env->FindClass(className);
jmethodID methodId = env->GetMethodID(clazz, methodName, descriptor);
env->DeleteLocalRef(clazz);
return methodId;
}
[[maybe_unused]] static bool AppendJString(JNIEnv* env, jstring text,
struct ExpandableString* dst) {
const char* utfText = env->GetStringUTFChars(text, NULL);
if (utfText == NULL) {
return false;
}
bool success = ExpandableStringAppend(dst, utfText);
env->ReleaseStringUTFChars(text, utfText);
return success;
}
/*
* Returns a human-readable summary of an exception object. The buffer will
* be populated with the "binary" class name and, if present, the
* exception message.
*/
[[maybe_unused]] static bool GetExceptionSummary(JNIEnv* env, jthrowable thrown,
struct ExpandableString* dst) {
// Summary is <exception_class_name> ": " <exception_message>
jclass exceptionClass = env->GetObjectClass(thrown); // Always succeeds
jmethodID getName = FindMethod(env, "java/lang/Class", "getName", "()Ljava/lang/String;");
jstring className = (jstring)env->CallObjectMethod(exceptionClass, getName);
if (className == NULL) {
ExpandableStringAssign(dst, "<error getting class name>");
env->ExceptionClear();
env->DeleteLocalRef(exceptionClass);
return false;
}
env->DeleteLocalRef(exceptionClass);
exceptionClass = NULL;
if (!AppendJString(env, className, dst)) {
ExpandableStringAssign(dst, "<error getting class name UTF-8>");
env->ExceptionClear();
env->DeleteLocalRef(className);
return false;
}
env->DeleteLocalRef(className);
className = NULL;
jmethodID getMessage =
FindMethod(env, "java/lang/Throwable", "getMessage", "()Ljava/lang/String;");
jstring message = (jstring)env->CallObjectMethod(thrown, getMessage);
if (message == NULL) {
return true;
}
bool success = (ExpandableStringAppend(dst, ": ") && AppendJString(env, message, dst));
if (!success) {
// Two potential reasons for reaching here:
//
// 1. managed heap allocation failure (OOME).
// 2. native heap allocation failure for the storage in |dst|.
//
// Attempt to append failure notification, okay to fail, |dst| contains the class name
// of |thrown|.
ExpandableStringAppend(dst, "<error getting message>");
// Clear OOME if present.
env->ExceptionClear();
}
env->DeleteLocalRef(message);
message = NULL;
return success;
}
[[maybe_unused]] static jobject NewStringWriter(JNIEnv* env) {
jclass clazz = env->FindClass("java/io/StringWriter");
jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
jobject instance = env->NewObject(clazz, init);
env->DeleteLocalRef(clazz);
return instance;
}
[[maybe_unused]] static jstring StringWriterToString(JNIEnv* env, jobject stringWriter) {
jmethodID toString =
FindMethod(env, "java/io/StringWriter", "toString", "()Ljava/lang/String;");
return (jstring)env->CallObjectMethod(stringWriter, toString);
}
[[maybe_unused]] static jobject NewPrintWriter(JNIEnv* env, jobject writer) {
jclass clazz = env->FindClass("java/io/PrintWriter");
jmethodID init = env->GetMethodID(clazz, "<init>", "(Ljava/io/Writer;)V");
jobject instance = env->NewObject(clazz, init, writer);
env->DeleteLocalRef(clazz);
return instance;
}
[[maybe_unused]] static bool GetStackTrace(JNIEnv* env, jthrowable thrown,
struct ExpandableString* dst) {
// This function is equivalent to the following Java snippet:
// StringWriter sw = new StringWriter();
// PrintWriter pw = new PrintWriter(sw);
// thrown.printStackTrace(pw);
// String trace = sw.toString();
// return trace;
jobject sw = NewStringWriter(env);
if (sw == NULL) {
return false;
}
jobject pw = NewPrintWriter(env, sw);
if (pw == NULL) {
env->DeleteLocalRef(sw);
return false;
}
jmethodID printStackTrace =
FindMethod(env, "java/lang/Throwable", "printStackTrace", "(Ljava/io/PrintWriter;)V");
env->CallVoidMethod(thrown, printStackTrace, pw);
jstring trace = StringWriterToString(env, sw);
env->DeleteLocalRef(pw);
pw = NULL;
env->DeleteLocalRef(sw);
sw = NULL;
if (trace == NULL) {
return false;
}
bool success = AppendJString(env, trace, dst);
env->DeleteLocalRef(trace);
return success;
}
[[maybe_unused]] static void GetStackTraceOrSummary(JNIEnv* env, jthrowable thrown,
struct ExpandableString* dst) {
// This method attempts to get a stack trace or summary info for an exception.
// The exception may be provided in the |thrown| argument to this function.
// If |thrown| is NULL, then any pending exception is used if it exists.
// Save pending exception, callees may raise other exceptions. Any pending exception is
// rethrown when this function exits.
jthrowable pendingException = env->ExceptionOccurred();
if (pendingException != NULL) {
env->ExceptionClear();
}
if (thrown == NULL) {
if (pendingException == NULL) {
ExpandableStringAssign(dst, "<no pending exception>");
return;
}
thrown = pendingException;
}
if (!GetStackTrace(env, thrown, dst)) {
// GetStackTrace may have raised an exception, clear it since it's not for the caller.
env->ExceptionClear();
GetExceptionSummary(env, thrown, dst);
}
if (pendingException != NULL) {
// Re-throw the pending exception present when this method was called.
env->Throw(pendingException);
env->DeleteLocalRef(pendingException);
}
}
[[maybe_unused]] static void DiscardPendingException(JNIEnv* env, const char* className) {
jthrowable exception = env->ExceptionOccurred();
env->ExceptionClear();
if (exception == NULL) {
return;
}
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
GetExceptionSummary(env, exception, &summary);
const char* details = (summary.data != NULL) ? summary.data : "Unknown";
__android_log_print(ANDROID_LOG_WARN, "JNIHelp",
"Discarding pending exception (%s) to throw %s", details, className);
ExpandableStringRelease(&summary);
env->DeleteLocalRef(exception);
}
[[maybe_unused]] static int ThrowException(JNIEnv* env, const char* className, const char* ctorSig,
...) {
int status = -1;
jclass exceptionClass = NULL;
va_list args;
va_start(args, ctorSig);
DiscardPendingException(env, className);
{
/* We want to clean up local references before returning from this function, so,
* regardless of return status, the end block must run. Have the work done in a
* nested block to avoid using any uninitialized variables in the end block. */
exceptionClass = env->FindClass(className);
if (exceptionClass == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "JNIHelp", "Unable to find exception class %s",
className);
/* an exception, most likely ClassNotFoundException, will now be pending */
goto end;
}
jmethodID init = env->GetMethodID(exceptionClass, "<init>", ctorSig);
if (init == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "JNIHelp",
"Failed to find constructor for '%s' '%s'", className, ctorSig);
goto end;
}
jobject instance = env->NewObjectV(exceptionClass, init, args);
if (instance == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "JNIHelp", "Failed to construct '%s'",
className);
goto end;
}
if (env->Throw((jthrowable)instance) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, "JNIHelp", "Failed to throw '%s'", className);
/* an exception, most likely OOM, will now be pending */
goto end;
}
/* everything worked fine, just update status to success and clean up */
status = 0;
}
end:
va_end(args);
if (exceptionClass != NULL) {
env->DeleteLocalRef(exceptionClass);
}
return status;
}
[[maybe_unused]] static jstring CreateExceptionMsg(JNIEnv* env, const char* msg) {
jstring detailMessage = env->NewStringUTF(msg);
if (detailMessage == NULL) {
/* Not really much we can do here. We're probably dead in the water,
but let's try to stumble on... */
env->ExceptionClear();
}
return detailMessage;
}
} // namespace android::jnihelp
/*
* Register one or more native methods with a particular class. "className" looks like
* "java/lang/String". Aborts on failure, returns 0 on success.
*/
[[maybe_unused]] static int jniRegisterNativeMethods(JNIEnv* env, const char* className,
const JNINativeMethod* methods,
int numMethods) {
using namespace android::jnihelp;
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
__android_log_assert("clazz == NULL", "JNIHelp",
"Native registration unable to find class '%s'; aborting...",
className);
}
int result = env->RegisterNatives(clazz, methods, numMethods);
env->DeleteLocalRef(clazz);
if (result == 0) {
return 0;
}
// Failure to register natives is fatal. Try to report the corresponding exception,
// otherwise abort with generic failure message.
jthrowable thrown = env->ExceptionOccurred();
if (thrown != NULL) {
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
if (GetExceptionSummary(env, thrown, &summary)) {
__android_log_print(ANDROID_LOG_FATAL, "JNIHelp", "%s", summary.data);
}
ExpandableStringRelease(&summary);
env->DeleteLocalRef(thrown);
}
__android_log_print(ANDROID_LOG_FATAL, "JNIHelp",
"RegisterNatives failed for '%s'; aborting...", className);
return result;
}
/*
* Throw an exception with the specified class and an optional 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.
*/
[[maybe_unused]] static int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
using namespace android::jnihelp;
jstring _detailMessage = CreateExceptionMsg(env, msg);
int _status = ThrowException(env, className, "(Ljava/lang/String;)V", _detailMessage);
if (_detailMessage != NULL) {
env->DeleteLocalRef(_detailMessage);
}
return _status;
}
/*
* Throw an android.system.ErrnoException, with the given function name and errno value.
*/
[[maybe_unused]] static int jniThrowErrnoException(JNIEnv* env, const char* functionName,
int errnum) {
using namespace android::jnihelp;
jstring _detailMessage = CreateExceptionMsg(env, functionName);
int _status = ThrowException(env, "android/system/ErrnoException", "(Ljava/lang/String;I)V",
_detailMessage, errnum);
if (_detailMessage != NULL) {
env->DeleteLocalRef(_detailMessage);
}
return _status;
}
/*
* 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.
*/
[[maybe_unused]] static int jniThrowExceptionFmt(JNIEnv* env, const char* className,
const char* fmt, ...) {
va_list args;
va_start(args, fmt);
char msgBuf[512];
vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
va_end(args);
return jniThrowException(env, className, msgBuf);
}
[[maybe_unused]] static int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
return jniThrowException(env, "java/lang/NullPointerException", msg);
}
[[maybe_unused]] static int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
return jniThrowException(env, "java/lang/RuntimeException", msg);
}
[[maybe_unused]] static int jniThrowIOException(JNIEnv* env, int errno_value) {
using namespace android::jnihelp;
char buffer[80];
const char* message = platformStrError(errno_value, buffer, sizeof(buffer));
return jniThrowException(env, "java/io/IOException", message);
}
/*
* 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).
*/
[[maybe_unused]] static inline jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars,
jsize len) {
return env->NewString(unicodeChars, len);
}
[[maybe_unused]] static inline jstring jniCreateString(JNIEnv* env, const char16_t* unicodeChars,
jsize len) {
return jniCreateString(env, reinterpret_cast<const jchar*>(unicodeChars), len);
}
/*
* Log a message and an exception.
* If exception is NULL, logs the current exception in the JNI environment.
*/
[[maybe_unused]] static void jniLogException(JNIEnv* env, int priority, const char* tag,
jthrowable exception = NULL) {
using namespace android::jnihelp;
struct ExpandableString summary;
ExpandableStringInitialize(&summary);
GetStackTraceOrSummary(env, exception, &summary);
const char* details = (summary.data != NULL) ? summary.data : "No memory to report exception";
__android_log_write(priority, tag, details);
ExpandableStringRelease(&summary);
}
#else // defined(__cplusplus)
// ART-internal only methods (not exported), exposed for legacy C users
int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods,
int numMethods);
void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable thrown);
int jniThrowException(JNIEnv* env, const char* className, const char* msg);
int jniThrowNullPointerException(JNIEnv* env, const char* msg);
#endif // defined(__cplusplus)

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2010 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
#include <nativehelper/scoped_local_frame.h>

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2010 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
#include "JNIHelp.h"
#include <nativehelper/scoped_local_ref.h>

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2010 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
#include "JNIHelp.h"
#include <nativehelper/scoped_primitive_array.h>

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2011 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
#include "JNIHelp.h"
#include <nativehelper/scoped_string_chars.h>

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2010 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
#include "JNIHelp.h"
#include <nativehelper/scoped_utf_chars.h>

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2011 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
#include <string>
#include <vector>
#include "JNIHelp.h"
#include "ScopedLocalRef.h"
template <typename StringVisitor>
jobjectArray toStringArray(JNIEnv* env, size_t count, StringVisitor&& visitor) {
jclass stringClass = env->FindClass("java/lang/String");
ScopedLocalRef<jobjectArray> result(env, env->NewObjectArray(count, stringClass, NULL));
env->DeleteLocalRef(stringClass);
if (result == nullptr) {
return nullptr;
}
for (size_t i = 0; i < count; ++i) {
ScopedLocalRef<jstring> s(env, env->NewStringUTF(visitor(i)));
if (env->ExceptionCheck()) {
return nullptr;
}
env->SetObjectArrayElement(result.get(), i, s.get());
if (env->ExceptionCheck()) {
return nullptr;
}
}
return result.release();
}
inline jobjectArray toStringArray(JNIEnv* env, const std::vector<std::string>& strings) {
return toStringArray(env, strings.size(), [&strings](size_t i) { return strings[i].c_str(); });
}
inline jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
size_t count = 0;
for (; strings[count] != nullptr; ++count) {}
return toStringArray(env, count, [&strings](size_t i) { return strings[i]; });
}
template <typename Counter, typename Getter>
jobjectArray toStringArray(JNIEnv* env, Counter* counter, Getter* getter) {
return toStringArray(env, counter(), [getter](size_t i) { return getter(i); });
}
#endif // __cplusplus

1141
include_jni/jni.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2007 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.
*/
/*
* JNI helper functions.
*
* This file may be included by C or C++ code, which is trouble because jni.h
* uses different typedefs for JNIEnv in each language.
*/
#pragma once
#include <sys/cdefs.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
__BEGIN_DECLS
/*
* Gets the managed heap array backing a java.nio.Buffer instance.
*
* Returns nullptr if there is no array backing.
*
* This method performs a JNI call to java.nio.NIOAccess.getBaseArray().
*/
jarray jniGetNioBufferBaseArray(C_JNIEnv* env, jobject nioBuffer);
/*
* Gets the offset of the current buffer position in bytes from the start of the managed heap
* array backing the buffer.
*
* Returns 0 if there is no array backing.
*
* This method performs a JNI call to java.nio.NIOAccess.getBaseArrayOffset().
*/
jint jniGetNioBufferBaseArrayOffset(C_JNIEnv* env, jobject nioBuffer);
/*
* Gets field information from a java.nio.Buffer instance.
*
* Reads the |position|, |limit|, and |elementSizeShift| fields from the buffer instance.
*
* Returns the |address| field of the java.nio.Buffer instance which is only valid (non-zero) when
* the buffer is backed by a direct buffer.
*/
jlong jniGetNioBufferFields(C_JNIEnv* env,
jobject nioBuffer,
/*out*/jint* position,
/*out*/jint* limit,
/*out*/jint* elementSizeShift);
/*
* Gets the current position from a java.nio.Buffer as a pointer to memory in a fixed buffer.
*
* Returns 0 if |nioBuffer| is not backed by a direct buffer.
*
* This method reads the |address|, |position|, and |elementSizeShift| fields from the
* java.nio.Buffer instance to calculate the pointer address for the current position.
*/
jlong jniGetNioBufferPointer(C_JNIEnv* env, jobject nioBuffer);
/*
* Clear the cache of constants libnativehelper is using.
*/
void jniUninitializeConstants();
__END_DECLS
/*
* For C++ code, we provide inlines that map to the C functions. g++ always
* inlines these, even on non-optimized builds.
*/
#if defined(__cplusplus)
#include <android/file_descriptor_jni.h>
inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
jobject fileDescriptor = AFileDescriptor_create(env);
if (fileDescriptor != nullptr) {
AFileDescriptor_setFd(env, fileDescriptor, fd);
}
return fileDescriptor;
}
inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
if (fileDescriptor == nullptr) {
return -1;
}
return AFileDescriptor_getFd(env, fileDescriptor);
}
inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
if (fileDescriptor == nullptr) {
jniThrowNullPointerException(env, "fileDescriptor is null");
return;
}
AFileDescriptor_setFd(env, fileDescriptor, value);
}
inline jarray jniGetNioBufferBaseArray(JNIEnv* env, jobject nioBuffer) {
return jniGetNioBufferBaseArray(&env->functions, nioBuffer);
}
inline jint jniGetNioBufferBaseArrayOffset(JNIEnv* env, jobject nioBuffer) {
return jniGetNioBufferBaseArrayOffset(&env->functions, nioBuffer);
}
inline jlong jniGetNioBufferFields(JNIEnv* env, jobject nioBuffer,
jint* position, jint* limit, jint* elementSizeShift) {
return jniGetNioBufferFields(&env->functions, nioBuffer,
position, limit, elementSizeShift);
}
inline jlong jniGetNioBufferPointer(JNIEnv* env, jobject nioBuffer) {
return jniGetNioBufferPointer(&env->functions, nioBuffer);
}
#endif // defined(__cplusplus)

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2013 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
#include <sys/cdefs.h>
#include <stdbool.h>
__BEGIN_DECLS
/*
* The JNI invocation API exists to allow a choice of library responsible for managing virtual
* machines.
*/
/*
* Opaque structure used to hold JNI invocation internal state.
*/
struct JniInvocationImpl;
/*
* Creates an instance of a JniInvocationImpl.
*/
struct JniInvocationImpl* JniInvocationCreate();
/*
* Associates a library with a JniInvocationImpl instance. The library should export C symbols for
* JNI_GetDefaultJavaVMInitArgs, JNI_CreateJavaVM and JNI_GetDefaultJavaVMInitArgs.
*
* The specified |library| should be the filename of a shared library. The |library| is opened with
* dlopen(3).
*
* If there is an error opening the specified |library|, then function will fallback to the
* default library "libart.so". If the fallback library is successfully used then a warning is
* written to the Android log buffer. Use of the fallback library is not considered an error.
*
* If the fallback library cannot be opened or the expected symbols are not found in the library
* opened, then an error message is written to the Android log buffer and the function returns 0.
*
* Returns true on success, false otherwise.
*/
bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library);
/*
* Release resources associated with JniInvocationImpl instance.
*/
void JniInvocationDestroy(struct JniInvocationImpl* instance);
/*
* Gets the default library for JNI invocation. The default library is "libart.so". This value may
* be overridden for debuggable builds using the persist.sys.dalvik.vm.lib.2 system property.
*
* The |library| argument is the preferred library to use on debuggable builds (when
* ro.debuggable=1). If the |library| argument is nullptr, then the system preferred value will be
* queried from persist.sys.dalvik.vm.lib.2 if the caller has provided |buffer| argument.
*
* The |buffer| argument is used for reading system properties in debuggable builds. It is
* optional, but should be provisioned to be PROP_VALUE_MAX bytes if provided to ensure it is
* large enough to hold a system property.
*
* Returns the filename of the invocation library determined from the inputs and system
* properties. The returned value may be |library|, |buffer|, or a pointer to a string constant
* "libart.so".
*/
const char* JniInvocationGetLibrary(const char* library, char* buffer);
__END_DECLS
#ifdef __cplusplus
// JniInvocation adds a layer of indirection for applications using
// the JNI invocation API to allow the JNI implementation to be
// selected dynamically. Apps can specify a specific implementation to
// be used by calling InitJniInvocation. If this is not done, the
// 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 final {
public:
JniInvocation() {
impl_ = JniInvocationCreate();
}
~JniInvocation() {
JniInvocationDestroy(impl_);
}
// Initialize JNI invocation API. library should specify 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) {
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) {
return JniInvocationGetLibrary(library, buffer);
}
private:
JniInvocation(const JniInvocation&) = delete;
JniInvocation& operator=(const JniInvocation&) = delete;
JniInvocationImpl* impl_;
};
#endif // __cplusplus

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,283 @@
/*
* Copyright (C) 2017 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.
*/
/**
* Compile-time, zero-cost checking of JNI signatures against their C++ function type.
* This can trigger compile-time assertions if any of the input is invalid:
* (a) The signature specified does not conform to the JNI function descriptor syntax.
* (b) The C++ function is itself an invalid JNI function (e.g. missing JNIEnv*, etc).
* (c) The descriptor does not match the C++ function (e.g. "()V" will not match jint(jint)).
*
* The fundamental macros are as following:
* MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD - Create a checked JNINativeMethod{name, sig, func}.
* MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Same as above, but infer the JNI signature.
*
* Usage examples:
* // path/to/package/KlassName.java
* class KlassName {
* native jobject normal(int x);
* @FastNative native jobject fast(int x);
* @CriticalNative native int critical(long ptr);
* }
* // path_to_package_KlassName.cpp
* jobject KlassName_normal(JNIEnv*,jobject,jint) {...}
* jobject KlassName_fast(JNIEnv*,jobject,jint) {...}
* jint KlassName_critical(jlong) {...}
*
* // Manually specify each signature:
* JNINativeMethod[] gMethods = {
* MAKE_JNI_NATIVE_METHOD("normal", "(I)Ljava/lang/Object;", KlassName_normal),
* MAKE_JNI_FAST_NATIVE_METHOD("fast", "(I)Ljava/lang/Object;", KlassName_fast),
* MAKE_JNI_CRITICAL_NATIVE_METHOD("critical", "(Z)I", KlassName_critical),
* };
*
* // Automatically infer the signature:
* JNINativeMethod[] gMethodsAutomaticSignature = {
* MAKE_JNI_NATIVE_METHOD_AUTOSIG("normal", KlassName_normal),
* MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG("fast", KlassName_fast),
* MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG("critical", KlassName_critical),
* };
*
* // and then call JNIEnv::RegisterNatives with gMethods as usual.
*
* For convenience the following macros are defined:
* [FAST_|CRITICAL_]NATIVE_METHOD - Return JNINativeMethod for class, func name, and signature.
* OVERLOADED_[FAST_|CRITICAL_]NATIVE_METHOD - Same as above but allows a separate func identifier.
* [FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Return JNINativeMethod, sig inferred from function.
*
* The FAST_ prefix corresponds to functions annotated with @FastNative,
* and the CRITICAL_ prefix corresponds to functions annotated with @CriticalNative.
* See dalvik.annotation.optimization.CriticalNative for more details.
*
* =======================================
* Checking rules
* =======================================
*
* ---------------------------------------
* JNI descriptor syntax for functions
*
* Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification
* under the subsection "Type Signatures" table entry "method type".
*
* JNI signatures not conforming to the above syntax are rejected.
* ---------------------------------------
* C++ function types
*
* A normal or @FastNative JNI function type must be of the form
*
* ReturnType (JNIEnv*, jclass|jobject, [ArgTypes...]) {}
*
* A @CriticalNative JNI function type:
*
* must be of the form... ReturnType ([ArgTypes...]){}
* and must not contain any Reference Types.
*
* Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification
* under the subsection "Primitive Types" and "Reference Types" for the list
* of valid argument/return types.
*
* C++ function types not conforming to the above requirements are rejected.
* ---------------------------------------
* Matching of C++ function type against JNI function descriptor.
*
* Assuming all of the above conditions are met for signature and C++ type validity,
* then matching between the signature and the type validity can occur:
*
* Given a signature (Args...)Ret and the
* C++ function type of the form "CRet fn(JNIEnv*, jclass|jobject, CArgs...)",
* or for @CriticalNative of the form "CRet fn(CArgs...)"
*
* The number of Args... and the number of CArgs... must be equal.
*
* If so, attempt to match every component from the signature and function type
* against each other:
*
* ReturnType:
* V <-> void
* ArgumentType
*
* ArgumentType:
* PrimitiveType
* ReferenceType [except for @CriticalNative]
*
* PrimitiveType:
* Z <-> jboolean
* B <-> jbyte
* C <-> jchar
* S <-> jshort
* I <-> jint
* J <-> jlong
* F <-> jfloat
* D <-> jdouble
*
* ReferenceType:
* Ljava/lang/String; <-> jstring
* Ljava/lang/Class; <-> jclass
* L*; <- jobject
* Ljava/lang/Throwable; -> jthrowable
* L*; <- jthrowable
* [ PrimitiveType <-> ${CPrimitiveType}Array
* [ ReferenceType <-> jobjectArray
* [* <- jarray
*
* Wherein <-> represents a strong match (if the left or right pattern occurs,
* then left must match right, otherwise matching fails). <- and -> represent
* weak matches (that is, other match rules can be still attempted).
*
* Sidenote: Whilst a jobject could also represent a jclass, jstring, etc,
* the stricter approach is taken: the most exact C++ type must be used.
*/
#pragma once
// The below basic macros do not perform automatic stringification,
// invoked e.g. as MAKE_JNI_NATIVE_METHOD("some_name", "()V", void_fn)
// An expression that evaluates to JNINativeMethod { name, signature, function },
// and applies the above compile-time checking for signature+function.
// The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative.
#define MAKE_JNI_NATIVE_METHOD(name, signature, function) \
_NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function)
// An expression that evaluates to JNINativeMethod { name, signature, function },
// and applies the above compile-time checking for signature+function.
// The equivalent Java Language code must be annotated with @FastNative.
#define MAKE_JNI_FAST_NATIVE_METHOD(name, signature, function) \
_NATIVEHELPER_JNI_MAKE_METHOD(kFastNative, name, signature, function)
// An expression that evaluates to JNINativeMethod { name, signature, function },
// and applies the above compile-time checking for signature+function.
// The equivalent Java Language code must be annotated with @CriticalNative.
#define MAKE_JNI_CRITICAL_NATIVE_METHOD(name, signature, function) \
_NATIVEHELPER_JNI_MAKE_METHOD(kCriticalNative, name, signature, function)
// Automatically signature-inferencing macros are also available,
// which also checks the C++ function types for validity:
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
// by inferring the signature at compile-time. Only works when the C++ function type
// corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???).
//
// The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative.
#define MAKE_JNI_NATIVE_METHOD_AUTOSIG(name, function) \
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kNormalNative, name, function)
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
// by inferring the signature at compile-time. Only works when the C++ function type
// corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???).
//
// The equivalent Java Language code must be annotated with @FastNative.
#define MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(name, function) \
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kFastNative, name, function)
// An expression that evalutes to JNINativeMethod { name, infersig(function), function) }
// by inferring the signature at compile-time.
//
// The equivalent Java Language code must be annotated with @CriticalNative.
#define MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(name, function) \
_NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kCriticalNative, name, function)
// Convenience macros when the functions follow the naming convention:
// .java file .cpp file
// JavaLanguageName <-> ${ClassName}_${JavaLanguageName}
//
// Stringification is done automatically, invoked as:
// NATIVE_[FAST_|CRITICAL]_METHOD(ClassName, JavaLanguageName, Signature)
//
// Intended to construct a JNINativeMethod.
// (Assumes the C name is the ClassName_JavaMethodName).
//
// The Java Language code must be annotated with one of (none,@FastNative,@CriticalNative)
// for the (none,FAST_,CRITICAL_) variants of these macros.
#define NATIVE_METHOD(className, functionName, signature) \
MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \
MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
#define NATIVE_METHOD_AUTOSIG(className, functionName) \
MAKE_JNI_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
#define FAST_NATIVE_METHOD(className, functionName, signature) \
MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
#define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \
MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
#define FAST_NATIVE_METHOD_AUTOSIG(className, functionName) \
MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
#define CRITICAL_NATIVE_METHOD(className, functionName, signature) \
MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName)
#define OVERLOADED_CRITICAL_NATIVE_METHOD(className, functionName, signature, identifier) \
MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier)
#define CRITICAL_NATIVE_METHOD_AUTOSIG(className, functionName) \
MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName)
////////////////////////////////////////////////////////
// IMPLEMENTATION ONLY.
// DO NOT USE DIRECTLY.
////////////////////////////////////////////////////////
#if defined(__cplusplus) && __cplusplus >= 201402L
#include "nativehelper/detail/signature_checker.h" // for MAKE_CHECKED_JNI_NATIVE_METHOD
#endif
// Expands to an expression whose type is JNINativeMethod.
// This is for older versions of C++ or C, so it has no compile-time checking.
#define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) \
( \
(JNINativeMethod) { \
(name), \
(sig), \
_NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \
} \
)
// C++14 or better, use compile-time checking.
#if defined(__cplusplus) && __cplusplus >= 201402L
// Expands to a compound expression whose type is JNINativeMethod.
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn)
// Expands to a compound expression whose type is JNINativeMethod.
#define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \
MAKE_INFERRED_JNI_NATIVE_METHOD(kind, name, function)
#else
// Older versions of C++ or C code get the regular macro that's unchecked.
// Expands to a compound expression whose type is JNINativeMethod.
#define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \
_NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn)
// Need C++14 or newer to use the AUTOSIG macros.
#define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \
static_assert(false, "Cannot infer JNI signatures prior to C++14 for function " #function);
#endif // C++14 check
// C-style cast for C, C++-style cast for C++ to avoid warnings/errors.
#if defined(__cplusplus)
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
which_cast<to>
#else
#define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \
(to)
#endif

30
libnativehelper.map.txt Normal file
View File

@ -0,0 +1,30 @@
LIBNATIVEHELPER_S { # introduced=S
global:
# NDK API for libnativehelper.
AFileDescriptor_create;
AFileDescriptor_getFd;
AFileDescriptor_setFd;
# JNI Invocation methods available to platform and apps.
JNI_CreateJavaVM;
JNI_GetDefaultJavaVMInitArgs;
JNI_GetCreatedJavaVMs;
local:
*;
};
LIBNATIVEHELPER_PLATFORM { # platform-only
global:
JniInvocationCreate;
JniInvocationDestroy;
JniInvocationInit;
JniInvocationGetLibrary;
jniGetNioBufferBaseArray;
jniGetNioBufferBaseArrayOffset;
jniGetNioBufferPointer;
jniGetNioBufferFields;
jniUninitializeConstants;
};

275
libnativehelper_lazy.c Normal file
View File

@ -0,0 +1,275 @@
/*
* Copyright (C) 2021 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 "libnativehelper_lazy.h"
#include <dlfcn.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include "jni.h"
#include "log/log.h"
#include "android/file_descriptor_jni.h"
#include "nativehelper/JNIHelp.h"
#include "nativehelper/JNIPlatformHelp.h"
#include "nativehelper/JniInvocation.h"
// This file provides a lazy interface to libnativehelper.so to address early boot dependencies.
// Specifically bootanimation now runs before the ART APEX is loaded and libnativehelper.so is
// in the ART APEX. bootanimation does not call any code in libnativehelper.
// Method pointers to libnativehelper methods are held in array which simplifies checking
// all pointers are initialized.
enum MethodIndex {
// NDK file descriptor API in file_descriptor_jni.h.
k_AFileDescriptor_create,
k_AFileDescriptor_getFd,
k_AFileDescriptor_setFd,
// JNI_Invocation API declared in jni.h.
k_JNI_CreateJavaVM,
k_JNI_GetCreatedJavaVMs,
k_JNI_GetDefaultJavaVMInitArgs,
// Methods in JNIPlatformHelp.h.
k_jniGetNioBufferBaseArray,
k_jniGetNioBufferBaseArrayOffset,
k_jniGetNioBufferFields,
k_jniGetNioBufferPointer,
k_jniUninitializeConstants,
// Methods in JniInvocation.h.
k_JniInvocationCreate,
k_JniInvocationDestroy,
k_JniInvocationGetLibrary,
k_JniInvocationInit,
// Marker for count of methods
k_MethodCount
};
// Table of methods pointers in libnativehelper APIs.
static void* g_Methods[k_MethodCount];
//
// Libnativehelper lazy loading.
//
static atomic_bool gPreventLibnativehelperLoading = false; // Allows tests to block loading.
void PreventLibnativehelperLazyLoadingForTests() {
atomic_store_explicit(&gPreventLibnativehelperLoading, true, memory_order_release);
}
static void* LoadLibnativehelper(int dlopen_flags) {
if (atomic_load_explicit(&gPreventLibnativehelperLoading, memory_order_acquire)) {
return NULL;
}
return dlopen("libnativehelper.so", dlopen_flags);
}
static bool IsLibnativehelperLoaded() {
return LoadLibnativehelper(RTLD_NOLOAD) != NULL;
}
//
// Initialization and symbol binding.
//
static void BindSymbol(void* handle, const char* name, enum MethodIndex index) {
void* symbol = dlsym(handle, name);
LOG_ALWAYS_FATAL_IF(symbol == NULL,
"Failed to find symbol '%s' in libnativehelper.so: %s", name, dlerror());
g_Methods[index] = symbol;
}
static void InitializeOnce() {
void* handle = LoadLibnativehelper(RTLD_NOW);
LOG_ALWAYS_FATAL_IF(handle == NULL, "Failed to load libnativehelper.so: %s", dlerror());
#undef BIND_SYMBOL
#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_ ## name);
// NDK file descriptor API in file_descriptor_jni.h.
BIND_SYMBOL(AFileDescriptor_create);
BIND_SYMBOL(AFileDescriptor_getFd);
BIND_SYMBOL(AFileDescriptor_setFd);
// JNI_Invocation API declared in jni.h.
BIND_SYMBOL(JNI_CreateJavaVM);
BIND_SYMBOL(JNI_GetCreatedJavaVMs);
BIND_SYMBOL(JNI_GetDefaultJavaVMInitArgs);
// Methods in JNIPlatformHelp.h.
BIND_SYMBOL(jniGetNioBufferBaseArray);
BIND_SYMBOL(jniGetNioBufferBaseArrayOffset);
BIND_SYMBOL(jniGetNioBufferFields);
BIND_SYMBOL(jniGetNioBufferPointer);
BIND_SYMBOL(jniUninitializeConstants);
// Methods in JniInvocation.h.
BIND_SYMBOL(JniInvocationCreate);
BIND_SYMBOL(JniInvocationDestroy);
BIND_SYMBOL(JniInvocationGetLibrary);
BIND_SYMBOL(JniInvocationInit);
#undef BIND_SYMBOL
// Check every symbol is bound.
for (int i = 0; i < k_MethodCount; ++i) {
LOG_ALWAYS_FATAL_IF(g_Methods[i] == NULL,
"Uninitialized method in libnativehelper_lazy at index: %d", i);
}
}
static void EnsureInitialized() {
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
pthread_once(&initialized, InitializeOnce);
}
#define INVOKE_METHOD(name, method_type, args...) \
do { \
EnsureInitialized(); \
void* method = g_Methods[k_ ## name]; \
return ((method_type) method)(args); \
} while (0)
#define INVOKE_VOID_METHOD(name, method_type, args...) \
do { \
EnsureInitialized(); \
void* method = g_Methods[k_ ## name]; \
((method_type) method)(args); \
} while (0)
//
// Forwarding for methods in file_descriptor_jni.h.
//
jobject AFileDescriptor_create(JNIEnv* env) {
typedef jobject (*M)(JNIEnv*);
INVOKE_METHOD(AFileDescriptor_create, M, env);
}
int AFileDescriptor_getFd(JNIEnv* env, jobject fileDescriptor) {
typedef int (*M)(JNIEnv*, jobject);
INVOKE_METHOD(AFileDescriptor_getFd, M, env, fileDescriptor);
}
void AFileDescriptor_setFd(JNIEnv* env, jobject fileDescriptor, int fd) {
typedef void (*M)(JNIEnv*, jobject, int);
INVOKE_VOID_METHOD(AFileDescriptor_setFd, M, env, fileDescriptor, fd);
}
//
// Forwarding for the JNI_Invocation API declarded in jni.h.
//
// Some code may attempt to use this JNI_Invocation API to establish if there is a VM (b/174768641).
// Because INVOKE_METHOD produces a fatal error if used before libnativehelper.so, we need some
// additional logic for the JNI_Invocation API to allow JNI_GetCreatedJavaVMs to be called even
// if libnativehelper.so is not loaded.
//
// Consequently, we use an atomic variable if a VM is created through this API. But note
// this is not the only way a JavaVM may be created so checking this flag alone is not enough.
static atomic_bool gJavaVmCreatedLazily = false;
static jint JNI_CreateJavaVMImpl(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
typedef jint (*M)(JavaVM**, JNIEnv**, void*);
INVOKE_METHOD(JNI_CreateJavaVM, M, p_vm, p_env, vm_args);
}
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
jint status = JNI_CreateJavaVMImpl(p_vm, p_env, vm_args);
if (status == JNI_OK) {
atomic_store_explicit(&gJavaVmCreatedLazily, true, memory_order_release);
}
return status;
}
jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
typedef jint (*M)(void*);
INVOKE_METHOD(JNI_GetDefaultJavaVMInitArgs, M, vm_args);
}
jint JNI_GetCreatedJavaVMs(JavaVM** p_vm, jsize vm_max, jsize* p_vm_count) {
typedef jint (*M)(JavaVM**, jsize, jsize*);
// If no VMs have been created created lazily and libnativehelper.so has not been loaded
// by other means, then fill-in the VM count as zero and return JNI_OK.
if (!atomic_load_explicit(&gJavaVmCreatedLazily, memory_order_acquire) &&
!IsLibnativehelperLoaded()) {
*p_vm_count = 0;
return JNI_OK;
}
INVOKE_METHOD(JNI_GetCreatedJavaVMs, M, p_vm, vm_max, p_vm_count);
}
//
// Forwarding for methods in JNIPlatformHelp.h.
//
jarray jniGetNioBufferBaseArray(JNIEnv* env, jobject nioBuffer) {
typedef jarray (*M)(JNIEnv*, jobject);
INVOKE_METHOD(jniGetNioBufferBaseArray, M, env, nioBuffer);
}
int jniGetNioBufferBaseArrayOffset(JNIEnv* env, jobject nioBuffer) {
typedef int (*M)(JNIEnv*, jobject);
INVOKE_METHOD(jniGetNioBufferBaseArrayOffset, M, env, nioBuffer);
}
jlong jniGetNioBufferFields(JNIEnv* env, jobject nioBuffer,
jint* position, jint* limit, jint* elementSizeShift) {
typedef jlong (*M)(JNIEnv*, jobject, jint*, jint*, jint*);
INVOKE_METHOD(jniGetNioBufferFields, M, env, nioBuffer, position, limit,
elementSizeShift);
}
jlong jniGetNioBufferPointer(JNIEnv* env, jobject nioBuffer) {
typedef jlong (*M)(JNIEnv*, jobject);
INVOKE_METHOD(jniGetNioBufferPointer, M, env, nioBuffer);
}
void jniUninitializeConstants() {
typedef void (*M)();
INVOKE_VOID_METHOD(jniUninitializeConstants, M);
}
//
// Forwarding for methods in JniInvocation.h.
//
struct JniInvocationImpl* JniInvocationCreate() {
typedef struct JniInvocationImpl* (*M)();
INVOKE_METHOD(JniInvocationCreate, M);
}
void JniInvocationDestroy(struct JniInvocationImpl* instance) {
typedef void (*M)(struct JniInvocationImpl*);
INVOKE_METHOD(JniInvocationDestroy, M, instance);
}
bool JniInvocationInit(struct JniInvocationImpl* instance, const char* library) {
typedef bool (*M)(struct JniInvocationImpl*, const char*);
INVOKE_METHOD(JniInvocationInit, M, instance, library);
}
const char* JniInvocationGetLibrary(const char* library, char* buffer) {
typedef const char* (*M)(const char*, char*);
INVOKE_METHOD(JniInvocationGetLibrary, M, library, buffer);
}

25
libnativehelper_lazy.h Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2021 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
#include <sys/cdefs.h>
__BEGIN_DECLS
void PreventLibnativehelperLazyLoadingForTests();
__END_DECLS

103
tests/Android.bp Normal file
View File

@ -0,0 +1,103 @@
// Build the unit tests.
package {
// http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// the below license kinds from "libnativehelper_license":
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["libnativehelper_license"],
}
cc_defaults {
name: "libnativehelper_test_defaults",
cflags: [
// Base set of cflags used by all things ART.
"-fno-rtti",
"-ggdb3",
"-Wall",
"-Werror",
"-Wextra",
"-Wstrict-aliasing",
"-fstrict-aliasing",
"-Wunreachable-code",
"-Wredundant-decls",
"-Wshadow",
"-Wunused",
"-fvisibility=protected",
// Warn about thread safety violations with clang.
"-Wthread-safety",
"-Wthread-safety-negative",
// Warn if switch fallthroughs aren't annotated.
"-Wimplicit-fallthrough",
// Enable float equality warnings.
"-Wfloat-equal",
// Enable warning of converting ints to void*.
"-Wint-to-void-pointer-cast",
// Enable warning for deprecated language features.
"-Wdeprecated",
// Disable warning from external/libcxxabi/include/cxxabi.h
"-Wno-deprecated-dynamic-exception-spec",
// Enable warning for unreachable break & return.
"-Wunreachable-code-break",
"-Wunreachable-code-return",
// Enable thread annotations for std::mutex, etc.
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
],
host_supported: true,
test_options: {
unit_test: true,
},
tidy: true,
}
cc_test {
name: "libnativehelper_tests",
defaults: ["libnativehelper_test_defaults"],
test_suites: ["general-tests"],
srcs: [
"scoped_local_frame_test.cpp",
"scoped_local_ref_test.cpp",
"scoped_primitive_array_test.cpp",
"libnativehelper_api_test.c",
"JniSafeRegisterNativeMethods_test.cpp",
],
shared_libs: ["libnativehelper"],
}
cc_test {
name: "libnativehelper_lazy_tests",
defaults: ["libnativehelper_test_defaults"],
test_suites: ["general-tests"],
srcs: ["libnativehelper_lazy_test.cpp"],
shared_libs: ["liblog"],
static_libs: ["libnativehelper_lazy"],
}
// Tests for internal functions that aren't present in the APEX stub API. Use
// `bootstrap:true` to bypass the stub library. This test won't link when
// prebuilts are preferred, because we cannot link against the source variant
// then.
// TODO(b/180107266): Enable in TEST_MAPPING. Also use a better way than
// `bootstrap:true` - `test_for` ought to work but fails because the test is
// host enabled so host variants of the APEXes are expected.
cc_test {
name: "libnativehelper_internal_tests",
defaults: [
"art_module_source_build_defaults",
"libnativehelper_test_defaults",
],
srcs: [
"ExpandableString_test.cpp",
"JniInvocation_test.cpp",
],
bootstrap: true,
shared_libs: ["libnativehelper"],
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2020 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 <array>
#include <string>
#include <gtest/gtest.h>
#include <string.h>
#include "../ExpandableString.h"
TEST(ExpandableString, InitializeAppendRelease) {
const char* kAhoy = "Ahoy!";
struct ExpandableString s;
ExpandableStringInitialize(&s);
EXPECT_TRUE(s.data == NULL);
EXPECT_EQ(s.dataSize, 0u);
EXPECT_TRUE(ExpandableStringAppend(&s, kAhoy));
EXPECT_TRUE(s.data != NULL);
EXPECT_GE(s.dataSize, strlen(kAhoy));
ExpandableStringRelease(&s);
EXPECT_TRUE(s.data == NULL);
EXPECT_GE(s.dataSize, 0u);
}
TEST(ExpandableString, InitializeWriteRelease) {
const char* kAhoy = "Ahoy!";
const char* kMercy = "Mercy, Mercy, Mercy!";
struct ExpandableString s;
ExpandableStringInitialize(&s);
EXPECT_TRUE(s.data == NULL);
EXPECT_EQ(s.dataSize, 0u);
EXPECT_TRUE(ExpandableStringAssign(&s, kAhoy));
EXPECT_TRUE(s.data != NULL);
EXPECT_GE(s.dataSize, strlen(kAhoy));
EXPECT_TRUE(ExpandableStringAssign(&s, kMercy));
EXPECT_TRUE(s.data != NULL);
EXPECT_GE(s.dataSize, strlen(kMercy));
EXPECT_TRUE(ExpandableStringAssign(&s, kAhoy));
EXPECT_TRUE(s.data != NULL);
EXPECT_GE(s.dataSize, strlen(kAhoy));
ExpandableStringRelease(&s);
EXPECT_TRUE(s.data == NULL);
EXPECT_GE(s.dataSize, 0u);
}
class ExpandableStringTestFixture : public :: testing::TestWithParam<size_t> {
protected:
struct ExpandableString expandableString;
};
TEST_P(ExpandableStringTestFixture, AppendTest) {
size_t step = GetParam();
std::array<std::string, 3> inputs = {
std::string(step, 'a'),
std::string(step, 'b'),
std::string(step, 'c'),
};
for (size_t offset = 0; offset < step; ++offset) {
ExpandableStringInitialize(&expandableString);
std::string pad(step - 1u, '_');
EXPECT_TRUE(ExpandableStringAppend(&expandableString, pad.c_str()));
for (size_t i = 0; i < 4096u; ++i) {
const std::string& appendee = inputs[i % inputs.size()];
EXPECT_TRUE(ExpandableStringAppend(&expandableString, appendee.c_str()));
size_t requiredSize = pad.size() + i * step + 1u;
EXPECT_GE(expandableString.dataSize, requiredSize);
}
size_t position = 0u;
for (char c : pad) {
EXPECT_EQ(c, expandableString.data[position]);
position++;
}
for (size_t i = 0; i < 4096; ++i) {
const std::string& expected = inputs[i % inputs.size()];
EXPECT_EQ(0, strncmp(expected.c_str(), expandableString.data + position, expected.size()));
position += expected.size();
}
ExpandableStringRelease(&expandableString);
}
}
INSTANTIATE_TEST_CASE_P(
AppendTest,
ExpandableStringTestFixture,
::testing::Values(
1, 2, 3, 4, 5, 11, 17
));

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2014 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 "../JniInvocation-priv.h"
#include <gtest/gtest.h>
#include <jni.h>
static const char* kDefaultJniInvocationLibrary = "libart.so";
static const char* kTestNonNull = "libartd.so";
static const char* kTestNonNull2 = "libartd2.so";
TEST(JNIInvocation, Debuggable) {
const char* result = JniInvocationGetLibraryWith(nullptr, true, kTestNonNull2);
EXPECT_STREQ(result, kTestNonNull2);
result = JniInvocationGetLibraryWith(kTestNonNull, true, kTestNonNull2);
EXPECT_STREQ(result, kTestNonNull);
result = JniInvocationGetLibraryWith(kTestNonNull, true, nullptr);
EXPECT_STREQ(result, kTestNonNull);
result = JniInvocationGetLibraryWith(nullptr, true, nullptr);
EXPECT_STREQ(result, kDefaultJniInvocationLibrary);
}
TEST(JNIInvocation, NonDebuggable) {
const char* result = JniInvocationGetLibraryWith(nullptr, false, kTestNonNull2);
EXPECT_STREQ(result, kDefaultJniInvocationLibrary);
result = JniInvocationGetLibraryWith(kTestNonNull, false, kTestNonNull2);
EXPECT_STREQ(result, kDefaultJniInvocationLibrary);
result = JniInvocationGetLibraryWith(kTestNonNull, false, nullptr);
EXPECT_STREQ(result, kDefaultJniInvocationLibrary);
result = JniInvocationGetLibraryWith(nullptr, false, nullptr);
EXPECT_STREQ(result, kDefaultJniInvocationLibrary);
}
TEST(JNIInvocation, GetDefaultJavaVMInitArgsBeforeInit) {
EXPECT_DEATH(JNI_GetDefaultJavaVMInitArgs(nullptr), "Runtime library not loaded.");
}
TEST(JNIInvocation, CreateJavaVMBeforeInit) {
JavaVM *vm;
JNIEnv *env;
EXPECT_DEATH(JNI_CreateJavaVM(&vm, &env, nullptr), "Runtime library not loaded.");
}
TEST(JNIInvocation, GetCreatedJavaVMsBeforeInit) {
jsize vm_count;
JavaVM *vm;
int status = JNI_GetCreatedJavaVMs(&vm, 1, &vm_count);
EXPECT_EQ(status, JNI_OK);
EXPECT_EQ(vm_count, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
// Do not use directly. Use the defaults below.
package {
// http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// the below license kinds from "libnativehelper_license":
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["libnativehelper_license"],
}
cc_library_headers {
name: "jni_gtest_headers",
host_supported: true,
export_include_dirs: ["base"],
}
cc_defaults {
name: "jni_gtest_defaults",
header_libs: ["jni_gtest_headers"],
shared_libs: ["libnativehelper"],
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2018 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
#include <memory>
#include <gtest/gtest.h>
#include <jni.h>
#include <nativehelper/JniInvocation.h>
namespace android {
// Example test setup following googletest docs:
//
// template <typename Provider>
// class TemplatedTest : public JNITestBase<Provider> {
// ...
// }
//
// typedef ::testing::Types<MockJNIProvider> Providers;
// TYPED_TEST_CASE(TemplatedTest, Providers);
//
// TYPED_TEST() {
// // Test code. Use "this->" to access TemplatedTest members.
// }
// Provider is a concept that must follow this structure:
//
// class JNIProvider {
// public:
// JNIProvider();
//
// void SetUp();
// JNIEnv* CreateJNIEnv();
//
// void DestroyJNIEnv(JNIEnv* env);
// void TearDown();
// }
template <typename Provider, typename Test = ::testing::Test>
class JNITestBase : public Test {
protected:
JNITestBase() : provider_(), env_(nullptr), java_vm_(nullptr) {
}
void SetUp() override {
Test::SetUp();
provider_.SetUp();
env_ = provider_.CreateJNIEnv();
ASSERT_TRUE(env_ != nullptr);
}
void TearDown() override {
provider_->DestroyJNIEnv(env_);
provider_->TearDown();
Test::TearDown();
}
protected:
Provider provider_;
JNIEnv* env_;
JavaVM* java_vm_;
};
// A mockable implementation of the Provider concept. It is the responsibility
// of the test to stub out any needed functions (all function pointers will be
// null initially).
//
// TODO: Consider googlemock.
class MockJNIProvider {
public:
MockJNIProvider() {
}
void SetUp() {
// Nothing to here.
}
// TODO: Spawn threads to allow more envs?
JNIEnv* CreateJNIEnv() {
return CreateMockedJNIEnv().release();
}
void DestroyJNIEnv(JNIEnv* env) {
delete env->functions;
delete env;
}
void TearDown() {
// Nothing to do here.
}
protected:
std::unique_ptr<JNIEnv> CreateMockedJNIEnv() {
JNINativeInterface* inf = new JNINativeInterface();
memset(inf, 0, sizeof(JNINativeInterface));
std::unique_ptr<JNIEnv> ret(new JNIEnv{0});
ret->functions = inf;
return ret;
}
};
} // namespace android

View File

@ -0,0 +1,26 @@
/*
* 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/JNIHelp.h"
#include "nativehelper/JniInvocation.h"
#include "nativehelper/toStringArray.h"
int libnativehelper_api_test_main() {
// The test here is that the headers are properly guarded to support
// compilation with a C compiler.
return 0;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 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 "../libnativehelper_lazy.h"
#include <gtest/gtest.h>
#include "jni.h"
#include "nativehelper/JniInvocation.h"
#include "nativehelper/JNIHelp.h"
#include "nativehelper/JNIPlatformHelp.h"
// The tests here are just for the case when libnativehelper.so cannot be loaded by
// libnativehelper_lazy.
class LibnativehelperLazyTest : public ::testing::Test {
protected:
virtual void SetUp() {
::testing::Test::SetUp();
PreventLibnativehelperLazyLoadingForTests();
}
};
static const char* kLoadFailed = "Failed to load libnativehelper.so";
TEST_F(LibnativehelperLazyTest, NoLibnativehelperIsForJNIPlatformHelp) {
C_JNIEnv* env = NULL;
EXPECT_DEATH(jniGetNioBufferBaseArray(env, NULL), kLoadFailed);
EXPECT_DEATH(jniGetNioBufferBaseArrayOffset(env, NULL), kLoadFailed);
EXPECT_DEATH(jniGetNioBufferFields(env, NULL, NULL, NULL, NULL), kLoadFailed);
EXPECT_DEATH(jniGetNioBufferPointer(env, NULL), kLoadFailed);
EXPECT_DEATH(jniUninitializeConstants(), kLoadFailed);
}
TEST_F(LibnativehelperLazyTest, NoLibnativehelperIsForJniInvocation) {
EXPECT_DEATH(JniInvocationCreate(), kLoadFailed);
EXPECT_DEATH(JniInvocationDestroy(NULL), kLoadFailed);
EXPECT_DEATH(JniInvocationGetLibrary("a", NULL), kLoadFailed);
EXPECT_DEATH(JniInvocationInit(NULL, "a"), kLoadFailed);
}
TEST_F(LibnativehelperLazyTest, NoLibnativehelperIsForJniApi) {
PreventLibnativehelperLazyLoadingForTests();
JavaVM* vm = NULL;
JNIEnv* env = NULL;
jsize count = 0;
EXPECT_DEATH(JNI_GetDefaultJavaVMInitArgs(NULL), kLoadFailed);
EXPECT_DEATH(JNI_CreateJavaVM(&vm, &env, NULL), kLoadFailed);
EXPECT_EQ(JNI_OK, JNI_GetCreatedJavaVMs(&vm, 1, &count));
EXPECT_EQ(0, count);
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2020 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 "nativehelper/scoped_local_frame.h"
// This is a test that scoped headers work independently.
void TestScopedLocalFrame(JNIEnv* env) {
ScopedLocalFrame slf(env);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 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 "nativehelper/scoped_local_ref.h"
// This is a test that scoped headers work independently.
void TestScopedLocalRef(JNIEnv* env) {
ScopedLocalRef<jarray> slr1(env);
slr1.reset(nullptr);
slr1.get();
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 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 "nativehelper/scoped_primitive_array.h"
// This is a test that scoped headers work independently.
void TestScopedPrimitiveArrayRO(JNIEnv* env, jbooleanArray array) {
ScopedBooleanArrayRO sba(env, array);
sba.reset(nullptr);
sba.get();
sba.size();
}
void TestCompilationRW(JNIEnv* env, jintArray array) {
ScopedIntArrayRW sba(env, array);
sba.reset(nullptr);
sba.get();
sba.size();
sba[3] = 3;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 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 "nativehelper/scoped_string_chars.h"
// This is a test that scoped headers work independently.
void TestCompilationScopedStringChars(JNIEnv* env, string s) {
ScopedStringChars ssc(s);
ssc.get();
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 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 "nativehelper/scoped_utf_chars.h"
// This is a test that scoped headers work independently.
void TestCompilationScopedUtfChars(JNIEnv* env, jstring s) {
ScopedUtfChars suc(env, s);
suc.c_str();
}

72
tests_mts/Android.bp Normal file
View File

@ -0,0 +1,72 @@
// Copyright (C) 2020 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.
package {
// http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// the below license kinds from "libnativehelper_license":
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["libnativehelper_license"],
}
java_defaults {
name: "mts_libnativehelper_defaults",
compile_multilib: "both",
libs: [
"android.test.base.stubs",
],
static_libs: [
"ctstestrunner-axt",
"nativetesthelper",
],
jni_uses_platform_apis: true,
sdk_version: "test_current",
srcs: ["src/com/android/art/libnativehelper/JniHelpTest.java"],
test_suites: [
"general-tests",
"mts",
],
}
android_test {
name: "MtsLibnativehelperTestCases",
defaults: [
"cts_defaults",
"mts_libnativehelper_defaults",
],
jni_libs: [
"libnativehelper_mts_jni",
],
manifest: "AndroidManifest.xml",
srcs: [
"src/com/android/art/libnativehelper/LibnativehelperGTests.java",
],
}
// Same tests as "MtsLibnativehelperTestCases", but with the jni library
// linked against libnativehelper_lazy.a rather than libnativehelper.so.
android_test {
name: "MtsLibnativehelperLazyTestCases",
defaults: [
"cts_defaults",
"mts_libnativehelper_defaults",
],
jni_libs: [
"libnativehelper_lazy_mts_jni",
],
srcs: [
"src/com/android/art/libnativehelper/LibnativehelperLazyGTests.java",
],
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.art.libnativehelper"
android:targetSandboxVersion="2">
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.art.libnativehelper"
android:label="MTS tests for libnativehelper">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
</manifest>

5
tests_mts/OWNERS Normal file
View File

@ -0,0 +1,5 @@
# Bug component: 86431
enh@google.com
mast@google.com
ngeoffray@google.com
oth@google.com

51
tests_mts/README.md Normal file
View File

@ -0,0 +1,51 @@
# libnativehelper MTS tests
These tests cover the API surface of libnativehelper that is applicable once
the runtime is initialized.
These tests do not cover the API surface relating to the binding of the ART
runtime (DalvikVM), that preclude the initialization of the runtime, nor do
they cover JNI_CreateJavaVM(). These APIs have been invoked before the test
harness runs these tests.
API surface not directly covered here are:
```
JNI_GetCreatedJavaVMs
JniInvocationCreate
JniInvocationDestroy
JniInvocationInit
JniInvocationGetLibrary
jniUninitializeConstants
```
`JniInvocationInit()` is responsible for binding the ART runtime and
specifically the following methods:
```
JNI_CreateJavaVM
JNI_GetCreatedJavaVMs
JNI_GetDefaultJavaVMInitArgs
```
These tests do check that `JNI_GetCreatedJavaVMs()` and
`JNI_GetDefaultJavaVMInitArgs()` behave as expected and are thus asserted to
be correctly bound. `JNI_CreateJavaVM()` cannot be called in these tests
because Android only supports a single runtime per process.
`JniInvocationInit()` uses `JniInvocationGetLibrary()` to determine which
runtime to load (release, debug, or custom). The code responsible for that
decision is tested comprehensively in `libnativehelper_gtests`.
`jniUninitializeConstants` is only intended to be called when the runtime is
shutting down and unloading the managed core libraries.
## Potential Issues
The test harness depends on libnativehelper_compat_libc++ and the tests
depend on libnativehelper. The former library is a subset of libnativehelper.
There are potential ODR problems if the two libraries having overlapping
global state. It would be better to have two separate test suites for these
two libraries.

56
tests_mts/jni/Android.bp Normal file
View File

@ -0,0 +1,56 @@
// Copyright (C) 2020 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.
package {
// http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// the below license kinds from "libnativehelper_license":
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["libnativehelper_license"],
}
cc_defaults {
name: "libnativehelper_jni_defaults",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
],
srcs: [
"jni_invocation_test.cpp",
"jni_helper_jni.cpp",
"libnativehelper_test.cpp",
],
shared_libs: ["liblog"],
static_libs: ["libgmock_ndk"],
stl: "c++_static",
// libnativetesthelper_jni depends on libnativehelper_compat_libc++.
// At the time of writing there is no duplicated global state in the
// libnativehelper sources between these functions. Should this change,
// then there could be ODR problems here.
whole_static_libs: ["libnativetesthelper_jni"],
tidy: true,
}
cc_library_shared {
name: "libnativehelper_mts_jni",
defaults: ["libnativehelper_jni_defaults"],
shared_libs: ["libnativehelper"],
}
cc_library_shared {
name: "libnativehelper_lazy_mts_jni",
defaults: ["libnativehelper_jni_defaults"],
static_libs: ["libnativehelper_lazy"],
}

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2020 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 <iterator>
#include <dlfcn.h>
#include <jni.h>
#include <android/log.h>
#include <nativehelper/jni_macros.h>
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_string_chars.h>
#include <nativehelper/scoped_utf_chars.h>
#include <nativehelper/JNIPlatformHelp.h>
#include "libnativehelper_test.h"
namespace {
static void throwException(JNIEnv* env, jclass /*clazz*/, jstring className, jstring message) {
ScopedUtfChars c(env, className);
ScopedUtfChars m(env, message);
jniThrowException(env, c.c_str(), m.c_str());
}
static void throwExceptionWithIntFormat(JNIEnv* env,
jclass /*clazz*/,
jstring className,
jstring format,
jint value) {
ScopedUtfChars c(env, className);
ScopedUtfChars f(env, format);
jniThrowExceptionFmt(env, c.c_str(), f.c_str(), value);
}
static void throwNullPointerException(JNIEnv* env,
jclass /*clazz*/,
jstring message) {
ScopedUtfChars m(env, message);
jniThrowNullPointerException(env, m.c_str());
}
static void throwRuntimeException(JNIEnv* env, jclass /*clazz*/, jstring message) {
ScopedUtfChars m(env, message);
jniThrowRuntimeException(env, m.c_str());
}
static void throwIOException(JNIEnv* env, jclass /*clazz*/, jint cause) {
jniThrowIOException(env, cause);
}
static void throwErrnoException(JNIEnv* env, jclass /*clazz*/, jstring functionName, jint cause) {
ScopedUtfChars m(env, functionName);
jniThrowErrnoException(env, m.c_str(), cause);
}
static void logException(JNIEnv* env, jclass /*clazz*/, jthrowable throwable) {
jniLogException(env, ANDROID_LOG_VERBOSE, __FILE__, throwable);
}
static jobject fileDescriptorCreate(JNIEnv* env, jclass /*clazz*/, jint unix_fd) {
return jniCreateFileDescriptor(env, unix_fd);
}
static jint fileDescriptorGetFD(JNIEnv* env, jclass /*clazz*/, jobject jiofd) {
return jniGetFDFromFileDescriptor(env, jiofd);
}
static void fileDescriptorSetFD(JNIEnv* env, jclass /*clazz*/, jobject jiofd, jint unix_fd) {
jniSetFileDescriptorOfFD(env, jiofd, unix_fd);
}
static jstring createString(JNIEnv* env, jclass /*clazz*/, jstring value) {
ScopedStringChars ssc(env, value);
return jniCreateString(env, ssc.get(), ssc.size());
}
} // namespace
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
static const JNINativeMethod methods[] = {
MAKE_JNI_NATIVE_METHOD("throwException",
"(Ljava/lang/String;Ljava/lang/String;)V",
throwException),
MAKE_JNI_NATIVE_METHOD("throwExceptionWithIntFormat",
"(Ljava/lang/String;Ljava/lang/String;I)V",
throwExceptionWithIntFormat),
MAKE_JNI_NATIVE_METHOD("throwNullPointerException",
"(Ljava/lang/String;)V",
throwNullPointerException),
MAKE_JNI_NATIVE_METHOD("throwRuntimeException",
"(Ljava/lang/String;)V",
throwRuntimeException),
MAKE_JNI_NATIVE_METHOD("throwIOException",
"(I)V",
throwIOException),
MAKE_JNI_NATIVE_METHOD("throwErrnoException",
"(Ljava/lang/String;I)V",
throwErrnoException),
MAKE_JNI_NATIVE_METHOD("logException",
"(Ljava/lang/Throwable;)V",
logException),
MAKE_JNI_NATIVE_METHOD("fileDescriptorCreate",
"(I)Ljava/io/FileDescriptor;",
fileDescriptorCreate),
MAKE_JNI_NATIVE_METHOD("fileDescriptorGetFD",
"(Ljava/io/FileDescriptor;)I",
fileDescriptorGetFD),
MAKE_JNI_NATIVE_METHOD("fileDescriptorSetFD",
"(Ljava/io/FileDescriptor;I)V",
fileDescriptorSetFD),
MAKE_JNI_NATIVE_METHOD("createString",
"(Ljava/lang/String;)Ljava/lang/String;",
createString),
};
int rc = jniRegisterNativeMethods(env,
"com/android/art/libnativehelper/JniHelpTest",
methods,
std::size(methods));
if (rc != JNI_OK) return rc;
return JNI_VERSION_1_6;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 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 "libnativehelper_test.h"
TEST_F(LibnativehelperTest, GetCreatedJavaVMs) {
JavaVM* createdVMs[2] = { nullptr, nullptr };
jsize count;
ASSERT_NE(nullptr, mEnv);
ASSERT_EQ(JNI_OK, JNI_GetCreatedJavaVMs(&createdVMs[0], 2, &count));
ASSERT_EQ(1, count);
ASSERT_NE(nullptr, createdVMs[0]);
ASSERT_EQ(nullptr, createdVMs[1]);
JavaVM* currentVM;
ASSERT_EQ(JNI_OK, mEnv->GetJavaVM(&currentVM));
ASSERT_EQ(createdVMs[0], currentVM);
}
TEST_F(LibnativehelperTest, GetDefaultJavaVMInitArgs) {
JavaVMOption options[1];
JavaVMInitArgs initArgs;
initArgs.version = JNI_VERSION_1_6;
initArgs.nOptions = 0;
initArgs.options = options;
initArgs.ignoreUnrecognized = JNI_TRUE;
// ART does not support JNI_GetDefaultJavaVMInitArgs(), should this change it'll need a test.
ASSERT_EQ(JNI_ERR, JNI_GetDefaultJavaVMInitArgs(&initArgs));
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2020 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 <libnativehelper_test.h>
#include <nativetesthelper_jni/utils.h>
void LibnativehelperTest::SetUp() {
int result = GetJavaVM()->GetEnv(reinterpret_cast<void**>(&mEnv), JNI_VERSION_1_6);
EXPECT_EQ(JNI_OK, result);
EXPECT_NE(nullptr, mEnv);
}
void LibnativehelperTest::TearDown() {
mEnv = nullptr;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2020 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
#include <gtest/gtest.h>
#include <jni.h>
class LibnativehelperTest : public ::testing::Test {
protected:
virtual void SetUp() override;
virtual void TearDown() override;
JNIEnv* mEnv;
};

View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 2020 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.
*/
package com.android.art.libnativehelper;
import android.test.AndroidTestCase;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.nio.IntBuffer;
import java.nio.FloatBuffer;
import java.nio.DoubleBuffer;
import android.system.ErrnoException;
import org.junit.Assert;
public class JniHelpTest extends AndroidTestCase {
private static native void throwException(String className, String message);
private static native void throwExceptionWithIntFormat(String className,
String format,
int value);
private static native void throwNullPointerException(String message);
private static native void throwRuntimeException(String message);
private static native void throwIOException(int cause) throws IOException;
private static native void throwErrnoException(String fileName, int cause) throws ErrnoException;
private static native void logException(Throwable throwable);
private static native FileDescriptor fileDescriptorCreate(int unixFd);
private static native int fileDescriptorGetFD(FileDescriptor jiofd);
private static native void fileDescriptorSetFD(FileDescriptor jiofd, int unixFd);
private static native String createString(String input);
public void testThrowException() {
final String message = "Because test";
try {
throwException("java/lang/RuntimeException", message);
fail("Unreachable");
} catch (RuntimeException e) {
assertEquals(message, e.getMessage());
}
}
public void testThrowExceptionWithIntFormat() {
final String format = "Because test %d";
try {
throwExceptionWithIntFormat("java/lang/RuntimeException", format, 101);
fail("Unreachable");
} catch (RuntimeException e) {
assertEquals("Because test 101", e.getMessage());
}
}
public void testThrowNullPointerException() {
final String message = "Because another test";
try {
throwNullPointerException(message);
fail("Unreachable");
} catch (NullPointerException e) {
assertEquals(message, e.getMessage());
}
}
public void testThrowRuntimeException() {
final String message = "Because test again";
try {
throwRuntimeException(message);
fail("Unreachable");
} catch (RuntimeException e) {
assertEquals(message, e.getMessage());
}
}
public void testIOException() {
String s1 = null;
try {
throwIOException(70);
fail("Unreachable");
} catch (IOException e) {
s1 = e.getMessage();
}
assertNotNull(s1);
String s2 = null;
try {
throwIOException(71);
fail("Unreachable");
} catch (IOException e) {
s2 = e.getMessage();
}
assertNotNull(s2);
assertFalse(s1.equals(s2));
}
public void testErrnoException() {
final String functionName = "execve";
final int err = 42;
try {
throwErrnoException(functionName, err);
fail("Unreachable");
} catch (ErrnoException e) {
// The message contains the function name as well as the string for the errno, just only
// check the first part of the message
assertTrue("Function name", e.getMessage().startsWith(functionName));
assertEquals(err, e.errno);
}
}
public void testLogException() {
try {
throw new RuntimeException("Exception for logging test");
} catch (RuntimeException e) {
// TODO: Mock/redirect logcat to test output is logged appropriately.
// Or add extend JNIHelp API to write to a buffer or file.
logException(e);
}
}
public void testFileDescriptorCreate() {
int [] unix_fds = { -999, -1, 0, 1, 1000 };
for (int unix_fd : unix_fds) {
FileDescriptor f0 = fileDescriptorCreate(unix_fd);
assertNotNull(f0);
assertSame(f0.getClass(), FileDescriptor.class);
}
}
public void testFileDescriptorGetNull() {
assertEquals(-1, fileDescriptorGetFD(null));
}
public void testFileDescriptorGetNonNull() {
final int UNIX_FD = 77;
FileDescriptor jiofd = fileDescriptorCreate(UNIX_FD);
assertEquals(UNIX_FD, fileDescriptorGetFD(jiofd));
}
public void testFileDescriptorSetNull() {
try {
fileDescriptorSetFD(null, 1);
fail("Expected NullPointerException to be thrown.");
} catch (NullPointerException e) {}
}
public void testFileDescriptorSetNonNull() {
final int UNIX_FD = 127;
FileDescriptor jiofd = fileDescriptorCreate(0);
fileDescriptorSetFD(jiofd, UNIX_FD);
assertEquals(UNIX_FD, fileDescriptorGetFD(jiofd));
}
public void testCreateString() {
String input = "The treacherous mountain path lay ahead.";
String output = createString(input);
assertEquals(input, output);
assertNotSame(input, output);
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2020 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.
*/
package com.android.art.libnativehelper;
import com.android.gtestrunner.GtestRunner;
import com.android.gtestrunner.TargetLibrary;
import org.junit.runner.RunWith;
@RunWith(GtestRunner.class)
@TargetLibrary("nativehelper_mts_jni")
public class LibnativehelperGTests {}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2020 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.
*/
package com.android.art.libnativehelper;
import com.android.gtestrunner.GtestRunner;
import com.android.gtestrunner.TargetLibrary;
import org.junit.runner.RunWith;
@RunWith(GtestRunner.class)
@TargetLibrary("nativehelper_lazy_mts_jni")
public class LibnativehelperLazyGTests {}