blob: ff0935d2a084f0895de8dbdcebd0fd4c6c24c4df [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/jni/dart_jni.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/logging.h"
#include "flutter/lib/jni/jni_api.h"
#include "flutter/lib/jni/jni_array.h"
#include "flutter/lib/jni/jni_class.h"
#include "flutter/lib/jni/jni_object.h"
#include "flutter/lib/jni/jni_string.h"
#include "lib/tonic/dart_args.h"
#include "lib/tonic/dart_binding_macros.h"
#include "lib/tonic/converter/dart_converter.h"
namespace blink {
using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;
using tonic::DartConverter;
using tonic::StdStringToDart;
using tonic::ToDart;
namespace {
tonic::DartLibraryNatives* g_natives = nullptr;
Dart_NativeFunction GetNativeFunction(Dart_Handle name,
int argument_count,
bool* auto_setup_scope) {
return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope);
}
const uint8_t* GetSymbol(Dart_NativeFunction native_function) {
return g_natives->GetSymbol(native_function);
}
// Data cached from the Java VM.
struct DartJniJvmData {
ScopedJavaGlobalRef<jobject> class_loader;
ScopedJavaGlobalRef<jclass> class_clazz;
jmethodID class_loader_load_class_method_id;
jmethodID class_get_name_method_id;
};
DartJniJvmData* g_jvm_data = nullptr;
DartJniIsolateDataProvider g_isolate_data_provider = nullptr;
DartJniIsolateData* IsolateData() {
return g_isolate_data_provider();
}
} // anonymous namespace
// Check if a JNI API has thrown an exception. If so, convert it to a
// Dart exception.
bool CheckJniException(JNIEnv* env, Dart_Handle* exception) {
if (env->ExceptionCheck() == JNI_FALSE)
return false;
jthrowable java_throwable = env->ExceptionOccurred();
env->ExceptionClear();
std::string info = base::android::GetJavaExceptionInfo(env, java_throwable);
*exception = StdStringToDart(info);
return true;
}
// Check if a Dart API returned an error handle.
bool CheckDartException(Dart_Handle result, Dart_Handle* exception) {
if (!Dart_IsError(result))
return false;
*exception = result;
return true;
}
DART_NATIVE_CALLBACK_STATIC(JniApi, FromReflectedField);
DART_NATIVE_CALLBACK_STATIC(JniApi, FromReflectedMethod);
DART_NATIVE_CALLBACK_STATIC(JniApi, GetApplicationContext);
DART_NATIVE_CALLBACK_STATIC(JniApi, GetClassLoader);
DART_NATIVE_CALLBACK_STATIC(JniBooleanArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniByteArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniCharArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniClass, FromName);
DART_NATIVE_CALLBACK_STATIC(JniClass, FromClassObject);
DART_NATIVE_CALLBACK_STATIC(JniDoubleArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniFloatArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniIntArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniLongArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniObjectArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniShortArray, Create);
DART_NATIVE_CALLBACK_STATIC(JniString, Create);
#define FOR_EACH_BINDING(V) \
V(JniArray, GetLength) \
V(JniBooleanArray, GetArrayElement) \
V(JniBooleanArray, SetArrayElement) \
V(JniByteArray, GetArrayElement) \
V(JniByteArray, SetArrayElement) \
V(JniCharArray, GetArrayElement) \
V(JniCharArray, SetArrayElement) \
V(JniClass, CallStaticBooleanMethod) \
V(JniClass, CallStaticByteMethod) \
V(JniClass, CallStaticCharMethod) \
V(JniClass, CallStaticDoubleMethod) \
V(JniClass, CallStaticFloatMethod) \
V(JniClass, CallStaticIntMethod) \
V(JniClass, CallStaticLongMethod) \
V(JniClass, CallStaticObjectMethod) \
V(JniClass, CallStaticShortMethod) \
V(JniClass, CallStaticVoidMethod) \
V(JniClass, GetFieldId) \
V(JniClass, GetMethodId) \
V(JniClass, GetStaticBooleanField) \
V(JniClass, GetStaticByteField) \
V(JniClass, GetStaticCharField) \
V(JniClass, GetStaticDoubleField) \
V(JniClass, GetStaticFieldId) \
V(JniClass, GetStaticFloatField) \
V(JniClass, GetStaticIntField) \
V(JniClass, GetStaticLongField) \
V(JniClass, GetStaticMethodId) \
V(JniClass, GetStaticObjectField) \
V(JniClass, GetStaticShortField) \
V(JniClass, IsAssignable) \
V(JniClass, NewObject) \
V(JniClass, SetStaticBooleanField) \
V(JniClass, SetStaticByteField) \
V(JniClass, SetStaticCharField) \
V(JniClass, SetStaticDoubleField) \
V(JniClass, SetStaticFloatField) \
V(JniClass, SetStaticIntField) \
V(JniClass, SetStaticLongField) \
V(JniClass, SetStaticObjectField) \
V(JniClass, SetStaticShortField) \
V(JniDoubleArray, GetArrayElement) \
V(JniDoubleArray, SetArrayElement) \
V(JniFloatArray, GetArrayElement) \
V(JniFloatArray, SetArrayElement) \
V(JniIntArray, GetArrayElement) \
V(JniIntArray, SetArrayElement) \
V(JniLongArray, GetArrayElement) \
V(JniLongArray, SetArrayElement) \
V(JniObject, CallBooleanMethod) \
V(JniObject, CallByteMethod) \
V(JniObject, CallCharMethod) \
V(JniObject, CallDoubleMethod) \
V(JniObject, CallFloatMethod) \
V(JniObject, CallIntMethod) \
V(JniObject, CallLongMethod) \
V(JniObject, CallObjectMethod) \
V(JniObject, CallShortMethod) \
V(JniObject, CallVoidMethod) \
V(JniObject, GetBooleanField) \
V(JniObject, GetByteField) \
V(JniObject, GetCharField) \
V(JniObject, GetDoubleField) \
V(JniObject, GetFloatField) \
V(JniObject, GetIntField) \
V(JniObject, GetLongField) \
V(JniObject, GetObjectClass) \
V(JniObject, GetObjectField) \
V(JniObject, GetShortField) \
V(JniObject, SetBooleanField) \
V(JniObject, SetByteField) \
V(JniObject, SetCharField) \
V(JniObject, SetDoubleField) \
V(JniObject, SetFloatField) \
V(JniObject, SetIntField) \
V(JniObject, SetLongField) \
V(JniObject, SetObjectField) \
V(JniObject, SetShortField) \
V(JniObjectArray, GetArrayElement) \
V(JniObjectArray, SetArrayElement) \
V(JniShortArray, GetArrayElement) \
V(JniShortArray, SetArrayElement) \
V(JniString, GetText)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
void DartJni::InitForGlobal(DartJniIsolateDataProvider provider) {
if (!g_isolate_data_provider)
g_isolate_data_provider = provider;
if (!g_natives) {
g_natives = new tonic::DartLibraryNatives();
g_natives->Register(
{DART_REGISTER_NATIVE_STATIC(JniApi, FromReflectedField),
DART_REGISTER_NATIVE_STATIC(JniApi, FromReflectedMethod),
DART_REGISTER_NATIVE_STATIC(JniApi, GetApplicationContext),
DART_REGISTER_NATIVE_STATIC(JniApi, GetClassLoader),
DART_REGISTER_NATIVE_STATIC(JniBooleanArray, Create),
DART_REGISTER_NATIVE_STATIC(JniByteArray, Create),
DART_REGISTER_NATIVE_STATIC(JniCharArray, Create),
DART_REGISTER_NATIVE_STATIC(JniClass, FromName),
DART_REGISTER_NATIVE_STATIC(JniClass, FromClassObject),
DART_REGISTER_NATIVE_STATIC(JniDoubleArray, Create),
DART_REGISTER_NATIVE_STATIC(JniFloatArray, Create),
DART_REGISTER_NATIVE_STATIC(JniIntArray, Create),
DART_REGISTER_NATIVE_STATIC(JniLongArray, Create),
DART_REGISTER_NATIVE_STATIC(JniObjectArray, Create),
DART_REGISTER_NATIVE_STATIC(JniShortArray, Create),
DART_REGISTER_NATIVE_STATIC(JniString, Create),
FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
}
}
void DartJni::InitForIsolate() {
DCHECK(g_natives);
Dart_Handle jni_library = Dart_LookupLibrary(ToDart("dart:jni"));
DART_CHECK_VALID(jni_library)
DART_CHECK_VALID(
Dart_SetNativeResolver(jni_library, GetNativeFunction, GetSymbol));
Dart_Handle object_type =
Dart_GetType(jni_library, ToDart("JniObject"), 0, nullptr);
DART_CHECK_VALID(object_type);
IsolateData()->jni_object_type = Dart_NewPersistentHandle(object_type);
DART_CHECK_VALID(IsolateData()->jni_object_type);
Dart_Handle float_type =
Dart_GetType(jni_library, ToDart("JniFloat"), 0, nullptr);
DART_CHECK_VALID(float_type);
IsolateData()->jni_float_type = Dart_NewPersistentHandle(float_type);
DART_CHECK_VALID(IsolateData()->jni_float_type);
}
bool DartJni::InitJni() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(!g_jvm_data);
g_jvm_data = new DartJniJvmData();
g_jvm_data->class_loader.Reset(base::android::GetClassLoader(env));
ScopedJavaLocalRef<jclass> class_loader_clazz(
env, env->FindClass("java/lang/ClassLoader"));
CHECK(!base::android::ClearException(env));
g_jvm_data->class_loader_load_class_method_id =
env->GetMethodID(class_loader_clazz.obj(), "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
CHECK(!base::android::ClearException(env));
g_jvm_data->class_clazz.Reset(env, env->FindClass("java/lang/Class"));
CHECK(!base::android::ClearException(env));
g_jvm_data->class_get_name_method_id = env->GetMethodID(
g_jvm_data->class_clazz.obj(), "getName", "()Ljava/lang/String;");
CHECK(!base::android::ClearException(env));
return true;
}
void DartJni::OnThreadExit() {
base::android::DetachFromVM();
}
ScopedJavaLocalRef<jclass> DartJni::GetClass(JNIEnv* env, const char* name) {
jobject clazz = env->CallObjectMethod(
g_jvm_data->class_loader.obj(),
g_jvm_data->class_loader_load_class_method_id,
base::android::ConvertUTF8ToJavaString(env, name).obj());
return ScopedJavaLocalRef<jclass>(env, static_cast<jclass>(clazz));
}
std::string DartJni::GetObjectClassName(JNIEnv* env, jobject obj) {
jclass clazz = env->GetObjectClass(obj);
DCHECK(clazz);
jstring name = static_cast<jstring>(
env->CallObjectMethod(clazz, g_jvm_data->class_get_name_method_id));
DCHECK(name);
return base::android::ConvertJavaStringToUTF8(env, name);
}
jstring DartJni::DartToJavaString(JNIEnv* env,
Dart_Handle dart_string,
Dart_Handle* exception) {
if (!Dart_IsString(dart_string)) {
*exception = ToDart("Argument must be a string");
return nullptr;
}
intptr_t length;
Dart_Handle result = Dart_StringLength(dart_string, &length);
if (CheckDartException(result, exception))
return nullptr;
std::vector<uint16_t> string_data(length);
result = Dart_StringToUTF16(dart_string, string_data.data(), &length);
if (CheckDartException(result, exception))
return nullptr;
jstring java_string = env->NewString(string_data.data(), length);
CheckJniException(env, exception);
return java_string;
}
jobject DartJni::class_loader() {
return g_jvm_data->class_loader.obj();
}
jclass DartJni::class_clazz() {
return g_jvm_data->class_clazz.obj();
}
Dart_Handle DartJni::jni_object_type() {
Dart_Handle object_type =
Dart_HandleFromPersistent(IsolateData()->jni_object_type);
DCHECK(!Dart_IsError(object_type));
return object_type;
}
Dart_Handle DartJni::jni_float_type() {
Dart_Handle float_type =
Dart_HandleFromPersistent(IsolateData()->jni_float_type);
DCHECK(!Dart_IsError(float_type));
return float_type;
}
void JniMethodArgs::Convert(JNIEnv* env,
const std::vector<Dart_Handle>& dart_args,
Dart_Handle* exception) {
jvalues_.reserve(dart_args.size());
for (Dart_Handle dart_arg : dart_args) {
jvalue value = DartToJavaValue(env, dart_arg, exception);
if (*exception)
return;
jvalues_.push_back(value);
}
}
jvalue JniMethodArgs::DartToJavaValue(JNIEnv* env,
Dart_Handle dart_value,
Dart_Handle* exception) {
jvalue java_value = jvalue();
if (Dart_IsBoolean(dart_value)) {
java_value.z = DartConverter<bool>::FromDart(dart_value);
return java_value;
}
if (Dart_IsInteger(dart_value)) {
java_value.j = DartConverter<jlong>::FromDart(dart_value);
return java_value;
}
if (Dart_IsDouble(dart_value)) {
java_value.d = DartConverter<jdouble>::FromDart(dart_value);
return java_value;
}
if (Dart_IsString(dart_value)) {
java_value.l = DartJni::DartToJavaString(env, dart_value, exception);
return java_value;
}
if (Dart_IsNull(dart_value)) {
java_value.l = nullptr;
return java_value;
}
bool is_object;
Dart_Handle result =
Dart_ObjectIsType(dart_value, DartJni::jni_object_type(), &is_object);
if (CheckDartException(result, exception))
return java_value;
if (is_object) {
JniObject* jni_object = DartConverter<JniObject*>::FromDart(dart_value);
if (jni_object != nullptr) {
java_value.l = jni_object->java_object();
} else {
*exception = ToDart("Invalid JniObject argument");
}
return java_value;
}
bool is_float;
result = Dart_ObjectIsType(dart_value, DartJni::jni_float_type(), &is_float);
if (CheckDartException(result, exception))
return java_value;
if (is_float) {
Dart_Handle value_handle = Dart_GetField(dart_value, ToDart("value"));
if (CheckDartException(value_handle, exception))
return java_value;
double double_value;
result = Dart_DoubleValue(value_handle, &double_value);
if (CheckDartException(result, exception))
return java_value;
java_value.f = static_cast<jfloat>(double_value);
return java_value;
}
*exception = ToDart("Argument has unsupported data type");
return java_value;
}
} // namespace blink