// Copyright 2013 The Flutter 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/shell/platform/android/platform_view_android_jni_impl.h" #include #include #include #include #include #include "unicode/uchar.h" #include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" #include "flutter/fml/file.h" #include "flutter/fml/mapping.h" #include "flutter/fml/native_library.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/size.h" #include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/runtime/dart_service_isolate.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/apk_asset_provider.h" #include "flutter/shell/platform/android/flutter_main.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" #include "flutter/shell/platform/android/platform_view_android.h" #define ANDROID_SHELL_HOLDER \ (reinterpret_cast(shell_holder)) namespace flutter { namespace { bool CheckException(JNIEnv* env) { if (env->ExceptionCheck() == JNI_FALSE) { return true; } jthrowable exception = env->ExceptionOccurred(); env->ExceptionClear(); FML_LOG(ERROR) << fml::jni::GetJavaExceptionInfo(env, exception); env->DeleteLocalRef(exception); return false; } } // anonymous namespace static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; static fml::jni::ScopedJavaGlobalRef* g_flutter_jni_class = nullptr; static fml::jni::ScopedJavaGlobalRef* g_texture_wrapper_class = nullptr; // Called By Native static jmethodID g_flutter_callback_info_constructor = nullptr; jobject CreateFlutterCallbackInformation( JNIEnv* env, const std::string& callbackName, const std::string& callbackClassName, const std::string& callbackLibraryPath) { return env->NewObject(g_flutter_callback_info_class->obj(), g_flutter_callback_info_constructor, env->NewStringUTF(callbackName.c_str()), env->NewStringUTF(callbackClassName.c_str()), env->NewStringUTF(callbackLibraryPath.c_str())); } static jmethodID g_handle_platform_message_method = nullptr; static jmethodID g_handle_platform_message_response_method = nullptr; static jmethodID g_update_semantics_method = nullptr; static jmethodID g_update_custom_accessibility_actions_method = nullptr; static jmethodID g_on_first_frame_method = nullptr; static jmethodID g_on_engine_restart_method = nullptr; static jmethodID g_create_overlay_surface_method = nullptr; static jmethodID g_destroy_overlay_surfaces_method = nullptr; static jmethodID g_on_begin_frame_method = nullptr; static jmethodID g_on_end_frame_method = nullptr; static jmethodID g_attach_to_gl_context_method = nullptr; static jmethodID g_update_tex_image_method = nullptr; static jmethodID g_get_transform_matrix_method = nullptr; static jmethodID g_detach_from_gl_context_method = nullptr; static jmethodID g_compute_platform_resolved_locale_method = nullptr; static jmethodID g_request_dart_deferred_library_method = nullptr; // Called By Java static jmethodID g_on_display_platform_view_method = nullptr; // static jmethodID g_on_composite_platform_view_method = nullptr; static jmethodID g_on_display_overlay_surface_method = nullptr; static jmethodID g_overlay_surface_id_method = nullptr; static jmethodID g_overlay_surface_surface_method = nullptr; // Mutators static fml::jni::ScopedJavaGlobalRef* g_mutators_stack_class = nullptr; static jmethodID g_mutators_stack_init_method = nullptr; static jmethodID g_mutators_stack_push_transform_method = nullptr; static jmethodID g_mutators_stack_push_cliprect_method = nullptr; static jmethodID g_mutators_stack_push_cliprrect_method = nullptr; // Called By Java static jlong AttachJNI(JNIEnv* env, jclass clazz, jobject flutterJNI, jboolean is_background_view) { fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI); std::shared_ptr jni_facade = std::make_shared(java_object); auto shell_holder = std::make_unique( FlutterMain::Get().GetSettings(), jni_facade, is_background_view); if (shell_holder->IsValid()) { return reinterpret_cast(shell_holder.release()); } else { return 0; } } static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) { delete ANDROID_SHELL_HOLDER; } static void SurfaceCreated(JNIEnv* env, jobject jcaller, jlong shell_holder, jobject jsurface) { // Note: This frame ensures that any local references used by // ANativeWindow_fromSurface are released immediately. This is needed as a // workaround for https://code.google.com/p/android/issues/detail?id=68174 fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); auto window = fml::MakeRefCounted( ANativeWindow_fromSurface(env, jsurface)); ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window)); } static void SurfaceWindowChanged(JNIEnv* env, jobject jcaller, jlong shell_holder, jobject jsurface) { // Note: This frame ensures that any local references used by // ANativeWindow_fromSurface are released immediately. This is needed as a // workaround for https://code.google.com/p/android/issues/detail?id=68174 fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); auto window = fml::MakeRefCounted( ANativeWindow_fromSurface(env, jsurface)); ANDROID_SHELL_HOLDER->GetPlatformView()->NotifySurfaceWindowChanged( std::move(window)); } static void SurfaceChanged(JNIEnv* env, jobject jcaller, jlong shell_holder, jint width, jint height) { ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyChanged( SkISize::Make(width, height)); } static void SurfaceDestroyed(JNIEnv* env, jobject jcaller, jlong shell_holder) { ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyDestroyed(); } static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, jobject jcaller, jlong shell_holder, jstring jBundlePath, jstring jEntrypoint, jstring jLibraryUrl, jobject jAssetManager) { auto asset_manager = std::make_shared(); asset_manager->PushBack(std::make_unique( env, // jni environment jAssetManager, // asset manager fml::jni::JavaStringToString(env, jBundlePath)) // apk asset dir ); std::unique_ptr isolate_configuration; if (flutter::DartVM::IsRunningPrecompiledCode()) { isolate_configuration = IsolateConfiguration::CreateForAppSnapshot(); } else { std::unique_ptr kernel_blob = fml::FileMapping::CreateReadOnly( ANDROID_SHELL_HOLDER->GetSettings().application_kernel_asset); if (!kernel_blob) { FML_DLOG(ERROR) << "Unable to load the kernel blob asset."; return; } isolate_configuration = IsolateConfiguration::CreateForKernel(std::move(kernel_blob)); } RunConfiguration config(std::move(isolate_configuration), std::move(asset_manager)); { auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint); auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl); if ((entrypoint.size() > 0) && (libraryUrl.size() > 0)) { config.SetEntrypointAndLibrary(std::move(entrypoint), std::move(libraryUrl)); } else if (entrypoint.size() > 0) { config.SetEntrypoint(std::move(entrypoint)); } } ANDROID_SHELL_HOLDER->Launch(std::move(config)); } static jobject LookupCallbackInformation(JNIEnv* env, /* unused */ jobject, jlong handle) { auto cbInfo = flutter::DartCallbackCache::GetCallbackInformation(handle); if (cbInfo == nullptr) { return nullptr; } return CreateFlutterCallbackInformation(env, cbInfo->name, cbInfo->class_name, cbInfo->library_path); } static void SetViewportMetrics(JNIEnv* env, jobject jcaller, jlong shell_holder, jfloat devicePixelRatio, jint physicalWidth, jint physicalHeight, jint physicalPaddingTop, jint physicalPaddingRight, jint physicalPaddingBottom, jint physicalPaddingLeft, jint physicalViewInsetTop, jint physicalViewInsetRight, jint physicalViewInsetBottom, jint physicalViewInsetLeft, jint systemGestureInsetTop, jint systemGestureInsetRight, jint systemGestureInsetBottom, jint systemGestureInsetLeft) { const flutter::ViewportMetrics metrics{ static_cast(devicePixelRatio), static_cast(physicalWidth), static_cast(physicalHeight), static_cast(physicalPaddingTop), static_cast(physicalPaddingRight), static_cast(physicalPaddingBottom), static_cast(physicalPaddingLeft), static_cast(physicalViewInsetTop), static_cast(physicalViewInsetRight), static_cast(physicalViewInsetBottom), static_cast(physicalViewInsetLeft), static_cast(systemGestureInsetTop), static_cast(systemGestureInsetRight), static_cast(systemGestureInsetBottom), static_cast(systemGestureInsetLeft), }; ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); } static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( Rasterizer::ScreenshotType::UncompressedImage, false); if (screenshot.data == nullptr) { return nullptr; } const SkISize& frame_size = screenshot.frame_size; jsize pixels_size = frame_size.width() * frame_size.height(); jintArray pixels_array = env->NewIntArray(pixels_size); if (pixels_array == nullptr) { return nullptr; } jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); if (pixels == nullptr) { return nullptr; } auto* pixels_src = static_cast(screenshot.data->data()); // Our configuration of Skia does not support rendering to the // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). for (int i = 0; i < pixels_size; i++) { int32_t src_pixel = pixels_src[i]; uint8_t* src_bytes = reinterpret_cast(&src_pixel); std::swap(src_bytes[0], src_bytes[2]); pixels[i] = src_pixel; } env->ReleaseIntArrayElements(pixels_array, pixels, 0); jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); if (bitmap_class == nullptr) { return nullptr; } jmethodID create_bitmap = env->GetStaticMethodID( bitmap_class, "createBitmap", "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); if (create_bitmap == nullptr) { return nullptr; } jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); if (bitmap_config_class == nullptr) { return nullptr; } jmethodID bitmap_config_value_of = env->GetStaticMethodID( bitmap_config_class, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); if (bitmap_config_value_of == nullptr) { return nullptr; } jstring argb = env->NewStringUTF("ARGB_8888"); if (argb == nullptr) { return nullptr; } jobject bitmap_config = env->CallStaticObjectMethod( bitmap_config_class, bitmap_config_value_of, argb); if (bitmap_config == nullptr) { return nullptr; } return env->CallStaticObjectMethod(bitmap_class, create_bitmap, pixels_array, frame_size.width(), frame_size.height(), bitmap_config); } static void DispatchPlatformMessage(JNIEnv* env, jobject jcaller, jlong shell_holder, jstring channel, jobject message, jint position, jint responseId) { ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPlatformMessage( env, // fml::jni::JavaStringToString(env, channel), // message, // position, // responseId // ); } static void DispatchEmptyPlatformMessage(JNIEnv* env, jobject jcaller, jlong shell_holder, jstring channel, jint responseId) { ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchEmptyPlatformMessage( env, // fml::jni::JavaStringToString(env, channel), // responseId // ); } static void DispatchPointerDataPacket(JNIEnv* env, jobject jcaller, jlong shell_holder, jobject buffer, jint position) { uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); auto packet = std::make_unique(data, position); ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPointerDataPacket( std::move(packet)); } static void DispatchSemanticsAction(JNIEnv* env, jobject jcaller, jlong shell_holder, jint id, jint action, jobject args, jint args_position) { ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchSemanticsAction( env, // id, // action, // args, // args_position // ); } static void SetSemanticsEnabled(JNIEnv* env, jobject jcaller, jlong shell_holder, jboolean enabled) { ANDROID_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled(enabled); } static void SetAccessibilityFeatures(JNIEnv* env, jobject jcaller, jlong shell_holder, jint flags) { ANDROID_SHELL_HOLDER->GetPlatformView()->SetAccessibilityFeatures(flags); } static jboolean GetIsSoftwareRendering(JNIEnv* env, jobject jcaller) { return FlutterMain::Get().GetSettings().enable_software_rendering; } static void RegisterTexture(JNIEnv* env, jobject jcaller, jlong shell_holder, jlong texture_id, jobject surface_texture) { ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterExternalTexture( static_cast(texture_id), // fml::jni::JavaObjectWeakGlobalRef(env, surface_texture) // ); } static void MarkTextureFrameAvailable(JNIEnv* env, jobject jcaller, jlong shell_holder, jlong texture_id) { ANDROID_SHELL_HOLDER->GetPlatformView()->MarkTextureFrameAvailable( static_cast(texture_id)); } static void UnregisterTexture(JNIEnv* env, jobject jcaller, jlong shell_holder, jlong texture_id) { ANDROID_SHELL_HOLDER->GetPlatformView()->UnregisterTexture( static_cast(texture_id)); } static void InvokePlatformMessageResponseCallback(JNIEnv* env, jobject jcaller, jlong shell_holder, jint responseId, jobject message, jint position) { ANDROID_SHELL_HOLDER->GetPlatformView() ->InvokePlatformMessageResponseCallback(env, // responseId, // message, // position // ); } static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, jobject jcaller, jlong shell_holder, jint responseId) { ANDROID_SHELL_HOLDER->GetPlatformView() ->InvokePlatformMessageEmptyResponseCallback(env, // responseId // ); } static void NotifyLowMemoryWarning(JNIEnv* env, jobject obj, jlong shell_holder) { ANDROID_SHELL_HOLDER->NotifyLowMemoryWarning(); } static jboolean FlutterTextUtilsIsEmoji(JNIEnv* env, jobject obj, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI); } static jboolean FlutterTextUtilsIsEmojiModifier(JNIEnv* env, jobject obj, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER); } static jboolean FlutterTextUtilsIsEmojiModifierBase(JNIEnv* env, jobject obj, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_EMOJI_MODIFIER_BASE); } static jboolean FlutterTextUtilsIsVariationSelector(JNIEnv* env, jobject obj, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_VARIATION_SELECTOR); } static jboolean FlutterTextUtilsIsRegionalIndicator(JNIEnv* env, jobject obj, jint codePoint) { return u_hasBinaryProperty(codePoint, UProperty::UCHAR_REGIONAL_INDICATOR); } static void LoadLoadingUnitFailure(intptr_t loading_unit_id, std::string message, bool transient) { // TODO(garyq): Implement } static void DynamicFeatureInstallFailure(JNIEnv* env, jobject obj, jint jLoadingUnitId, jstring jError, jboolean jTransient) { LoadLoadingUnitFailure(static_cast(jLoadingUnitId), fml::jni::JavaStringToString(env, jError), static_cast(jTransient)); } static void LoadDartDeferredLibrary(JNIEnv* env, jobject obj, jlong shell_holder, jint jLoadingUnitId, jobjectArray jSearchPaths) { // Convert java->c++ intptr_t loading_unit_id = static_cast(jLoadingUnitId); std::vector search_paths = fml::jni::StringArrayToVector(env, jSearchPaths); // Use dlopen here to directly check if handle is nullptr before creating a // NativeLibrary. void* handle = nullptr; while (handle == nullptr && !search_paths.empty()) { std::string path = search_paths.back(); handle = ::dlopen(path.c_str(), RTLD_NOW); search_paths.pop_back(); } if (handle == nullptr) { LoadLoadingUnitFailure(loading_unit_id, "No lib .so found for provided search paths.", true); return; } fml::RefPtr native_lib = fml::NativeLibrary::CreateWithHandle(handle, false); // Resolve symbols. std::unique_ptr data_mapping = std::make_unique( native_lib, DartSnapshot::kIsolateDataSymbol); std::unique_ptr instructions_mapping = std::make_unique( native_lib, DartSnapshot::kIsolateInstructionsSymbol); ANDROID_SHELL_HOLDER->GetPlatformView()->LoadDartDeferredLibrary( loading_unit_id, std::move(data_mapping), std::move(instructions_mapping)); } // TODO(garyq): persist additional asset resolvers by updating instead of // replacing with newly created asset_manager static void UpdateAssetManager(JNIEnv* env, jobject obj, jlong shell_holder, jobject jAssetManager, jstring jAssetBundlePath) { auto asset_manager = std::make_shared(); asset_manager->PushBack(std::make_unique( env, // jni environment jAssetManager, // asset manager fml::jni::JavaStringToString(env, jAssetBundlePath)) // apk asset dir ); // Create config to set persistent cache asset manager RunConfiguration config(nullptr, std::move(asset_manager)); ANDROID_SHELL_HOLDER->GetPlatformView()->UpdateAssetManager( config.GetAssetManager()); } bool RegisterApi(JNIEnv* env) { static const JNINativeMethod flutter_jni_methods[] = { // Start of methods from FlutterJNI { .name = "nativeAttach", .signature = "(Lio/flutter/embedding/engine/FlutterJNI;Z)J", .fnPtr = reinterpret_cast(&AttachJNI), }, { .name = "nativeDestroy", .signature = "(J)V", .fnPtr = reinterpret_cast(&DestroyJNI), }, { .name = "nativeRunBundleAndSnapshotFromLibrary", .signature = "(JLjava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Landroid/content/res/AssetManager;)V", .fnPtr = reinterpret_cast(&RunBundleAndSnapshotFromLibrary), }, { .name = "nativeDispatchEmptyPlatformMessage", .signature = "(JLjava/lang/String;I)V", .fnPtr = reinterpret_cast(&DispatchEmptyPlatformMessage), }, { .name = "nativeDispatchPlatformMessage", .signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V", .fnPtr = reinterpret_cast(&DispatchPlatformMessage), }, { .name = "nativeInvokePlatformMessageResponseCallback", .signature = "(JILjava/nio/ByteBuffer;I)V", .fnPtr = reinterpret_cast(&InvokePlatformMessageResponseCallback), }, { .name = "nativeInvokePlatformMessageEmptyResponseCallback", .signature = "(JI)V", .fnPtr = reinterpret_cast( &InvokePlatformMessageEmptyResponseCallback), }, { .name = "nativeNotifyLowMemoryWarning", .signature = "(J)V", .fnPtr = reinterpret_cast(&NotifyLowMemoryWarning), }, // Start of methods from FlutterView { .name = "nativeGetBitmap", .signature = "(J)Landroid/graphics/Bitmap;", .fnPtr = reinterpret_cast(&GetBitmap), }, { .name = "nativeSurfaceCreated", .signature = "(JLandroid/view/Surface;)V", .fnPtr = reinterpret_cast(&SurfaceCreated), }, { .name = "nativeSurfaceWindowChanged", .signature = "(JLandroid/view/Surface;)V", .fnPtr = reinterpret_cast(&SurfaceWindowChanged), }, { .name = "nativeSurfaceChanged", .signature = "(JII)V", .fnPtr = reinterpret_cast(&SurfaceChanged), }, { .name = "nativeSurfaceDestroyed", .signature = "(J)V", .fnPtr = reinterpret_cast(&SurfaceDestroyed), }, { .name = "nativeSetViewportMetrics", .signature = "(JFIIIIIIIIIIIIII)V", .fnPtr = reinterpret_cast(&SetViewportMetrics), }, { .name = "nativeDispatchPointerDataPacket", .signature = "(JLjava/nio/ByteBuffer;I)V", .fnPtr = reinterpret_cast(&DispatchPointerDataPacket), }, { .name = "nativeDispatchSemanticsAction", .signature = "(JIILjava/nio/ByteBuffer;I)V", .fnPtr = reinterpret_cast(&DispatchSemanticsAction), }, { .name = "nativeSetSemanticsEnabled", .signature = "(JZ)V", .fnPtr = reinterpret_cast(&SetSemanticsEnabled), }, { .name = "nativeSetAccessibilityFeatures", .signature = "(JI)V", .fnPtr = reinterpret_cast(&SetAccessibilityFeatures), }, { .name = "nativeGetIsSoftwareRenderingEnabled", .signature = "()Z", .fnPtr = reinterpret_cast(&GetIsSoftwareRendering), }, { .name = "nativeRegisterTexture", .signature = "(JJLio/flutter/embedding/engine/renderer/" "SurfaceTextureWrapper;)V", .fnPtr = reinterpret_cast(&RegisterTexture), }, { .name = "nativeMarkTextureFrameAvailable", .signature = "(JJ)V", .fnPtr = reinterpret_cast(&MarkTextureFrameAvailable), }, { .name = "nativeUnregisterTexture", .signature = "(JJ)V", .fnPtr = reinterpret_cast(&UnregisterTexture), }, // Methods for Dart callback functionality. { .name = "nativeLookupCallbackInformation", .signature = "(J)Lio/flutter/view/FlutterCallbackInformation;", .fnPtr = reinterpret_cast(&LookupCallbackInformation), }, // Start of methods for FlutterTextUtils { .name = "nativeFlutterTextUtilsIsEmoji", .signature = "(I)Z", .fnPtr = reinterpret_cast(&FlutterTextUtilsIsEmoji), }, { .name = "nativeFlutterTextUtilsIsEmojiModifier", .signature = "(I)Z", .fnPtr = reinterpret_cast(&FlutterTextUtilsIsEmojiModifier), }, { .name = "nativeFlutterTextUtilsIsEmojiModifierBase", .signature = "(I)Z", .fnPtr = reinterpret_cast(&FlutterTextUtilsIsEmojiModifierBase), }, { .name = "nativeFlutterTextUtilsIsVariationSelector", .signature = "(I)Z", .fnPtr = reinterpret_cast(&FlutterTextUtilsIsVariationSelector), }, { .name = "nativeFlutterTextUtilsIsRegionalIndicator", .signature = "(I)Z", .fnPtr = reinterpret_cast(&FlutterTextUtilsIsRegionalIndicator), }, { .name = "nativeLoadDartDeferredLibrary", .signature = "(JI[Ljava/lang/String;)V", .fnPtr = reinterpret_cast(&LoadDartDeferredLibrary), }, { .name = "nativeUpdateAssetManager", .signature = "(JLandroid/content/res/AssetManager;Ljava/lang/String;)V", .fnPtr = reinterpret_cast(&UpdateAssetManager), }, { .name = "nativeDynamicFeatureInstallFailure", .signature = "(ILjava/lang/String;Z)V", .fnPtr = reinterpret_cast(&DynamicFeatureInstallFailure), }, }; if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, fml::size(flutter_jni_methods)) != 0) { FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI"; return false; } g_handle_platform_message_method = env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage", "(Ljava/lang/String;[BI)V"); if (g_handle_platform_message_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessage method"; return false; } g_handle_platform_message_response_method = env->GetMethodID( g_flutter_jni_class->obj(), "handlePlatformMessageResponse", "(I[B)V"); if (g_handle_platform_message_response_method == nullptr) { FML_LOG(ERROR) << "Could not locate handlePlatformMessageResponse method"; return false; } g_update_semantics_method = env->GetMethodID(g_flutter_jni_class->obj(), "updateSemantics", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_semantics_method == nullptr) { FML_LOG(ERROR) << "Could not locate updateSemantics method"; return false; } g_update_custom_accessibility_actions_method = env->GetMethodID( g_flutter_jni_class->obj(), "updateCustomAccessibilityActions", "(Ljava/nio/ByteBuffer;[Ljava/lang/String;)V"); if (g_update_custom_accessibility_actions_method == nullptr) { FML_LOG(ERROR) << "Could not locate updateCustomAccessibilityActions method"; return false; } g_on_first_frame_method = env->GetMethodID(g_flutter_jni_class->obj(), "onFirstFrame", "()V"); if (g_on_first_frame_method == nullptr) { FML_LOG(ERROR) << "Could not locate onFirstFrame method"; return false; } g_on_engine_restart_method = env->GetMethodID(g_flutter_jni_class->obj(), "onPreEngineRestart", "()V"); if (g_on_engine_restart_method == nullptr) { FML_LOG(ERROR) << "Could not locate onEngineRestart method"; return false; } g_create_overlay_surface_method = env->GetMethodID(g_flutter_jni_class->obj(), "createOverlaySurface", "()Lio/flutter/embedding/engine/FlutterOverlaySurface;"); if (g_create_overlay_surface_method == nullptr) { FML_LOG(ERROR) << "Could not locate createOverlaySurface method"; return false; } g_destroy_overlay_surfaces_method = env->GetMethodID( g_flutter_jni_class->obj(), "destroyOverlaySurfaces", "()V"); if (g_destroy_overlay_surfaces_method == nullptr) { FML_LOG(ERROR) << "Could not locate destroyOverlaySurfaces method"; return false; } fml::jni::ScopedJavaLocalRef overlay_surface_class( env, env->FindClass("io/flutter/embedding/engine/FlutterOverlaySurface")); if (overlay_surface_class.is_null()) { FML_LOG(ERROR) << "Could not locate FlutterOverlaySurface class"; return false; } g_overlay_surface_id_method = env->GetMethodID(overlay_surface_class.obj(), "getId", "()I"); if (g_overlay_surface_id_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterOverlaySurface#getId() method"; return false; } g_overlay_surface_surface_method = env->GetMethodID( overlay_surface_class.obj(), "getSurface", "()Landroid/view/Surface;"); if (g_overlay_surface_surface_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterOverlaySurface#getSurface() method"; return false; } return true; } bool PlatformViewAndroid::Register(JNIEnv* env) { if (env == nullptr) { FML_LOG(ERROR) << "No JNIEnv provided"; return false; } g_flutter_callback_info_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass("io/flutter/view/FlutterCallbackInformation")); if (g_flutter_callback_info_class->is_null()) { FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation class"; return false; } g_flutter_callback_info_constructor = env->GetMethodID( g_flutter_callback_info_class->obj(), "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); if (g_flutter_callback_info_constructor == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation constructor"; return false; } g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass("io/flutter/embedding/engine/FlutterJNI")); if (g_flutter_jni_class->is_null()) { FML_LOG(ERROR) << "Failed to find FlutterJNI Class."; return false; } g_mutators_stack_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass( "io/flutter/embedding/engine/mutatorsstack/FlutterMutatorsStack")); if (g_mutators_stack_class == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterMutatorsStack"; return false; } g_mutators_stack_init_method = env->GetMethodID(g_mutators_stack_class->obj(), "", "()V"); if (g_mutators_stack_init_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterMutatorsStack.init method"; return false; } g_mutators_stack_push_transform_method = env->GetMethodID(g_mutators_stack_class->obj(), "pushTransform", "([F)V"); if (g_mutators_stack_push_transform_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterMutatorsStack.pushTransform method"; return false; } g_mutators_stack_push_cliprect_method = env->GetMethodID( g_mutators_stack_class->obj(), "pushClipRect", "(IIII)V"); if (g_mutators_stack_push_cliprect_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterMutatorsStack.pushClipRect method"; return false; } g_mutators_stack_push_cliprrect_method = env->GetMethodID( g_mutators_stack_class->obj(), "pushClipRRect", "(IIII[F)V"); if (g_mutators_stack_push_cliprect_method == nullptr) { FML_LOG(ERROR) << "Could not locate FlutterMutatorsStack.pushClipRRect method"; return false; } g_on_display_platform_view_method = env->GetMethodID(g_flutter_jni_class->obj(), "onDisplayPlatformView", "(IIIIIIILio/flutter/embedding/engine/mutatorsstack/" "FlutterMutatorsStack;)V"); if (g_on_display_platform_view_method == nullptr) { FML_LOG(ERROR) << "Could not locate onDisplayPlatformView method"; return false; } g_on_begin_frame_method = env->GetMethodID(g_flutter_jni_class->obj(), "onBeginFrame", "()V"); if (g_on_begin_frame_method == nullptr) { FML_LOG(ERROR) << "Could not locate onBeginFrame method"; return false; } g_on_end_frame_method = env->GetMethodID(g_flutter_jni_class->obj(), "onEndFrame", "()V"); if (g_on_end_frame_method == nullptr) { FML_LOG(ERROR) << "Could not locate onEndFrame method"; return false; } g_on_display_overlay_surface_method = env->GetMethodID( g_flutter_jni_class->obj(), "onDisplayOverlaySurface", "(IIIII)V"); if (g_on_display_overlay_surface_method == nullptr) { FML_LOG(ERROR) << "Could not locate onDisplayOverlaySurface method"; return false; } g_texture_wrapper_class = new fml::jni::ScopedJavaGlobalRef( env, env->FindClass( "io/flutter/embedding/engine/renderer/SurfaceTextureWrapper")); if (g_texture_wrapper_class->is_null()) { FML_LOG(ERROR) << "Could not locate SurfaceTextureWrapper class"; return false; } g_attach_to_gl_context_method = env->GetMethodID( g_texture_wrapper_class->obj(), "attachToGLContext", "(I)V"); if (g_attach_to_gl_context_method == nullptr) { FML_LOG(ERROR) << "Could not locate attachToGlContext method"; return false; } g_update_tex_image_method = env->GetMethodID(g_texture_wrapper_class->obj(), "updateTexImage", "()V"); if (g_update_tex_image_method == nullptr) { FML_LOG(ERROR) << "Could not locate updateTexImage method"; return false; } g_get_transform_matrix_method = env->GetMethodID( g_texture_wrapper_class->obj(), "getTransformMatrix", "([F)V"); if (g_get_transform_matrix_method == nullptr) { FML_LOG(ERROR) << "Could not locate getTransformMatrix method"; return false; } g_detach_from_gl_context_method = env->GetMethodID( g_texture_wrapper_class->obj(), "detachFromGLContext", "()V"); if (g_detach_from_gl_context_method == nullptr) { FML_LOG(ERROR) << "Could not locate detachFromGlContext method"; return false; } g_compute_platform_resolved_locale_method = env->GetMethodID( g_flutter_jni_class->obj(), "computePlatformResolvedLocale", "([Ljava/lang/String;)[Ljava/lang/String;"); if (g_compute_platform_resolved_locale_method == nullptr) { FML_LOG(ERROR) << "Could not locate computePlatformResolvedLocale method"; return false; } g_request_dart_deferred_library_method = env->GetMethodID( g_flutter_jni_class->obj(), "requestDartDeferredLibrary", "(I)V"); if (g_request_dart_deferred_library_method == nullptr) { FML_LOG(ERROR) << "Could not locate requestDartDeferredLibrary method"; return false; } return RegisterApi(env); } PlatformViewAndroidJNIImpl::PlatformViewAndroidJNIImpl( fml::jni::JavaObjectWeakGlobalRef java_object) : java_object_(java_object) {} PlatformViewAndroidJNIImpl::~PlatformViewAndroidJNIImpl() = default; void PlatformViewAndroidJNIImpl::FlutterViewHandlePlatformMessage( fml::RefPtr message, int responseId) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } fml::jni::ScopedJavaLocalRef java_channel = fml::jni::StringToJavaString(env, message->channel()); if (message->hasData()) { fml::jni::ScopedJavaLocalRef message_array( env, env->NewByteArray(message->data().size())); env->SetByteArrayRegion( message_array.obj(), 0, message->data().size(), reinterpret_cast(message->data().data())); env->CallVoidMethod(java_object.obj(), g_handle_platform_message_method, java_channel.obj(), message_array.obj(), responseId); } else { env->CallVoidMethod(java_object.obj(), g_handle_platform_message_method, java_channel.obj(), nullptr, responseId); } FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewHandlePlatformMessageResponse( int responseId, std::unique_ptr data) { // We are on the platform thread. Attempt to get the strong reference to // the Java object. JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { // The Java object was collected before this message response got to // it. Drop the response on the floor. return; } if (data == nullptr) { // Empty response. env->CallVoidMethod(java_object.obj(), g_handle_platform_message_response_method, responseId, nullptr); } else { // Convert the vector to a Java byte array. fml::jni::ScopedJavaLocalRef data_array( env, env->NewByteArray(data->GetSize())); env->SetByteArrayRegion(data_array.obj(), 0, data->GetSize(), reinterpret_cast(data->GetMapping())); env->CallVoidMethod(java_object.obj(), g_handle_platform_message_response_method, responseId, data_array.obj()); } FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewUpdateSemantics( std::vector buffer, std::vector strings) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } fml::jni::ScopedJavaLocalRef direct_buffer( env, env->NewDirectByteBuffer(buffer.data(), buffer.size())); fml::jni::ScopedJavaLocalRef jstrings = fml::jni::VectorToStringArray(env, strings); env->CallVoidMethod(java_object.obj(), g_update_semantics_method, direct_buffer.obj(), jstrings.obj()); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewUpdateCustomAccessibilityActions( std::vector actions_buffer, std::vector strings) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } fml::jni::ScopedJavaLocalRef direct_actions_buffer( env, env->NewDirectByteBuffer(actions_buffer.data(), actions_buffer.size())); fml::jni::ScopedJavaLocalRef jstrings = fml::jni::VectorToStringArray(env, strings); env->CallVoidMethod(java_object.obj(), g_update_custom_accessibility_actions_method, direct_actions_buffer.obj(), jstrings.obj()); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewOnFirstFrame() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_on_first_frame_method); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewOnPreEngineRestart() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_on_engine_restart_method); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::SurfaceTextureAttachToGLContext( JavaWeakGlobalRef surface_texture, int textureId) { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef surface_texture_local_ref = surface_texture.get(env); if (surface_texture_local_ref.is_null()) { return; } env->CallVoidMethod(surface_texture_local_ref.obj(), g_attach_to_gl_context_method, textureId); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::SurfaceTextureUpdateTexImage( JavaWeakGlobalRef surface_texture) { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef surface_texture_local_ref = surface_texture.get(env); if (surface_texture_local_ref.is_null()) { return; } env->CallVoidMethod(surface_texture_local_ref.obj(), g_update_tex_image_method); FML_CHECK(CheckException(env)); } // The bounds we set for the canvas are post composition. // To fill the canvas we need to ensure that the transformation matrix // on the `SurfaceTexture` will be scaled to fill. We rescale and preseve // the scaled aspect ratio. SkSize ScaleToFill(float scaleX, float scaleY) { const double epsilon = std::numeric_limits::epsilon(); // scaleY is negative. const double minScale = fmin(scaleX, fabs(scaleY)); const double rescale = 1.0f / (minScale + epsilon); return SkSize::Make(scaleX * rescale, scaleY * rescale); } void PlatformViewAndroidJNIImpl::SurfaceTextureGetTransformMatrix( JavaWeakGlobalRef surface_texture, SkMatrix& transform) { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef surface_texture_local_ref = surface_texture.get(env); if (surface_texture_local_ref.is_null()) { return; } fml::jni::ScopedJavaLocalRef transformMatrix( env, env->NewFloatArray(16)); env->CallVoidMethod(surface_texture_local_ref.obj(), g_get_transform_matrix_method, transformMatrix.obj()); FML_CHECK(CheckException(env)); float* m = env->GetFloatArrayElements(transformMatrix.obj(), nullptr); float scaleX = m[0], scaleY = m[5]; const SkSize scaled = ScaleToFill(scaleX, scaleY); SkScalar matrix3[] = { scaled.fWidth, m[1], m[2], // m[4], scaled.fHeight, m[6], // m[8], m[9], m[10], // }; env->ReleaseFloatArrayElements(transformMatrix.obj(), m, JNI_ABORT); transform.set9(matrix3); } void PlatformViewAndroidJNIImpl::SurfaceTextureDetachFromGLContext( JavaWeakGlobalRef surface_texture) { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef surface_texture_local_ref = surface_texture.get(env); if (surface_texture_local_ref.is_null()) { return; } env->CallVoidMethod(surface_texture_local_ref.obj(), g_detach_from_gl_context_method); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewOnDisplayPlatformView( int view_id, int x, int y, int width, int height, int viewWidth, int viewHeight, MutatorsStack mutators_stack) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } jobject mutatorsStack = env->NewObject(g_mutators_stack_class->obj(), g_mutators_stack_init_method); std::vector>::const_iterator iter = mutators_stack.Begin(); while (iter != mutators_stack.End()) { switch ((*iter)->GetType()) { case transform: { const SkMatrix& matrix = (*iter)->GetMatrix(); SkScalar matrix_array[9]; matrix.get9(matrix_array); fml::jni::ScopedJavaLocalRef transformMatrix( env, env->NewFloatArray(9)); env->SetFloatArrayRegion(transformMatrix.obj(), 0, 9, matrix_array); env->CallVoidMethod(mutatorsStack, g_mutators_stack_push_transform_method, transformMatrix.obj()); break; } case clip_rect: { const SkRect& rect = (*iter)->GetRect(); env->CallVoidMethod( mutatorsStack, g_mutators_stack_push_cliprect_method, static_cast(rect.left()), static_cast(rect.top()), static_cast(rect.right()), static_cast(rect.bottom())); break; } case clip_rrect: { const SkRRect& rrect = (*iter)->GetRRect(); const SkRect& rect = rrect.rect(); const SkVector& upper_left = rrect.radii(SkRRect::kUpperLeft_Corner); const SkVector& upper_right = rrect.radii(SkRRect::kUpperRight_Corner); const SkVector& lower_right = rrect.radii(SkRRect::kLowerRight_Corner); const SkVector& lower_left = rrect.radii(SkRRect::kLowerLeft_Corner); SkScalar radiis[8] = { upper_left.x(), upper_left.y(), upper_right.x(), upper_right.y(), lower_right.x(), lower_right.y(), lower_left.x(), lower_left.y(), }; fml::jni::ScopedJavaLocalRef radiisArray( env, env->NewFloatArray(8)); env->SetFloatArrayRegion(radiisArray.obj(), 0, 8, radiis); env->CallVoidMethod( mutatorsStack, g_mutators_stack_push_cliprrect_method, (int)rect.left(), (int)rect.top(), (int)rect.right(), (int)rect.bottom(), radiisArray.obj()); break; } // TODO(cyanglaz): Implement other mutators. // https://github.com/flutter/flutter/issues/58426 case clip_path: case opacity: break; } ++iter; } env->CallVoidMethod(java_object.obj(), g_on_display_platform_view_method, view_id, x, y, width, height, viewWidth, viewHeight, mutatorsStack); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewDisplayOverlaySurface( int surface_id, int x, int y, int width, int height) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_on_display_overlay_surface_method, surface_id, x, y, width, height); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewBeginFrame() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_on_begin_frame_method); FML_CHECK(CheckException(env)); } void PlatformViewAndroidJNIImpl::FlutterViewEndFrame() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_on_end_frame_method); FML_CHECK(CheckException(env)); } std::unique_ptr PlatformViewAndroidJNIImpl::FlutterViewCreateOverlaySurface() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return nullptr; } fml::jni::ScopedJavaLocalRef overlay( env, env->CallObjectMethod(java_object.obj(), g_create_overlay_surface_method)); FML_CHECK(CheckException(env)); if (overlay.is_null()) { return std::make_unique(0, nullptr); } jint overlay_id = env->CallIntMethod(overlay.obj(), g_overlay_surface_id_method); jobject overlay_surface = env->CallObjectMethod(overlay.obj(), g_overlay_surface_surface_method); auto overlay_window = fml::MakeRefCounted( ANativeWindow_fromSurface(env, overlay_surface)); return std::make_unique( overlay_id, std::move(overlay_window)); } void PlatformViewAndroidJNIImpl::FlutterViewDestroyOverlaySurfaces() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return; } env->CallVoidMethod(java_object.obj(), g_destroy_overlay_surfaces_method); FML_CHECK(CheckException(env)); } std::unique_ptr> PlatformViewAndroidJNIImpl::FlutterViewComputePlatformResolvedLocale( std::vector supported_locales_data) { JNIEnv* env = fml::jni::AttachCurrentThread(); std::unique_ptr> out = std::make_unique>(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return out; } fml::jni::ScopedJavaLocalRef j_locales_data = fml::jni::VectorToStringArray(env, supported_locales_data); jobjectArray result = static_cast(env->CallObjectMethod( java_object.obj(), g_compute_platform_resolved_locale_method, j_locales_data.obj())); FML_CHECK(CheckException(env)); int length = env->GetArrayLength(result); for (int i = 0; i < length; i++) { out->emplace_back(fml::jni::JavaStringToString( env, static_cast(env->GetObjectArrayElement(result, i)))); } return out; } double PlatformViewAndroidJNIImpl::GetDisplayRefreshRate() { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return kUnknownDisplayRefreshRate; } jclass clazz = env->GetObjectClass(java_object.obj()); if (clazz == nullptr) { return kUnknownDisplayRefreshRate; } jfieldID fid = env->GetStaticFieldID(clazz, "refreshRateFPS", "F"); return static_cast(env->GetStaticFloatField(clazz, fid)); } bool PlatformViewAndroidJNIImpl::RequestDartDeferredLibrary( int loading_unit_id) { JNIEnv* env = fml::jni::AttachCurrentThread(); auto java_object = java_object_.get(env); if (java_object.is_null()) { return true; } env->CallObjectMethod(java_object.obj(), g_request_dart_deferred_library_method, loading_unit_id); FML_CHECK(CheckException(env)); return true; } } // namespace flutter