// 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 "flutter/fml/platform/android/scoped_java_ref.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/converter/dart_converter.h" #include "lib/tonic/dart_args.h" #include "lib/tonic/dart_binding_macros.h" namespace blink { using fml::jni::ScopedJavaGlobalRef; using fml::jni::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 class_loader; ScopedJavaGlobalRef 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 = fml::jni::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() { FTL_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 = fml::jni::AttachCurrentThread(); FTL_DCHECK(!g_jvm_data); g_jvm_data = new DartJniJvmData(); // TODO: The class loader must be set here. // g_jvm_data->class_loader.Reset(class_loader); ScopedJavaLocalRef class_loader_clazz( env, env->FindClass("java/lang/ClassLoader")); FTL_CHECK(!fml::jni::ClearException(env)); g_jvm_data->class_loader_load_class_method_id = env->GetMethodID(class_loader_clazz.obj(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); FTL_CHECK(!fml::jni::ClearException(env)); g_jvm_data->class_clazz.Reset(env, env->FindClass("java/lang/Class")); FTL_CHECK(!fml::jni::ClearException(env)); g_jvm_data->class_get_name_method_id = env->GetMethodID( g_jvm_data->class_clazz.obj(), "getName", "()Ljava/lang/String;"); FTL_CHECK(!fml::jni::ClearException(env)); return true; } void DartJni::OnThreadExit() { fml::jni::DetachFromVM(); } ScopedJavaLocalRef 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, fml::jni::StringToJavaString(env, name).obj()); return ScopedJavaLocalRef(env, static_cast(clazz)); } std::string DartJni::GetObjectClassName(JNIEnv* env, jobject obj) { jclass clazz = env->GetObjectClass(obj); FTL_DCHECK(clazz); jstring name = static_cast( env->CallObjectMethod(clazz, g_jvm_data->class_get_name_method_id)); FTL_DCHECK(name); return fml::jni::JavaStringToString(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 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); FTL_DCHECK(!Dart_IsError(object_type)); return object_type; } Dart_Handle DartJni::jni_float_type() { Dart_Handle float_type = Dart_HandleFromPersistent(IsolateData()->jni_float_type); FTL_DCHECK(!Dart_IsError(float_type)); return float_type; } void JniMethodArgs::Convert(JNIEnv* env, const std::vector& 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::FromDart(dart_value); return java_value; } if (Dart_IsInteger(dart_value)) { java_value.j = DartConverter::FromDart(dart_value); return java_value; } if (Dart_IsDouble(dart_value)) { java_value.d = DartConverter::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::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(double_value); return java_value; } *exception = ToDart("Argument has unsupported data type"); return java_value; } } // namespace blink