|  | // 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 |