| // Copyright 2015 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/shell/platform/android/platform_view_android.h" |
| |
| #include <android/native_window_jni.h> |
| #include <sys/resource.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| |
| #include <utility> |
| |
| #include "flutter/common/settings.h" |
| #include "flutter/common/threads.h" |
| #include "flutter/fml/platform/android/jni_util.h" |
| #include "flutter/fml/platform/android/scoped_java_ref.h" |
| #include "flutter/runtime/dart_service_isolate.h" |
| #include "flutter/shell/common/null_rasterizer.h" |
| #include "flutter/shell/gpu/gpu_rasterizer.h" |
| #include "flutter/shell/platform/android/android_external_texture_gl.h" |
| #include "flutter/shell/platform/android/android_surface_gl.h" |
| #include "flutter/shell/platform/android/android_surface_software.h" |
| #include "flutter/shell/platform/android/apk_asset_provider.h" |
| #include "flutter/shell/platform/android/platform_view_android_jni.h" |
| #include "flutter/shell/platform/android/vsync_waiter_android.h" |
| #include "lib/fxl/functional/make_copyable.h" |
| |
| #if SHELL_ENABLE_VULKAN |
| #include "flutter/shell/platform/android/android_surface_vulkan.h" |
| #endif // SHELL_ENABLE_VULKAN |
| |
| namespace shell { |
| |
| class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { |
| FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseAndroid); |
| |
| public: |
| void Complete(std::vector<uint8_t> data) override { |
| fxl::RefPtr<PlatformMessageResponseAndroid> self(this); |
| blink::Threads::Platform()->PostTask( |
| fxl::MakeCopyable([ self, data = std::move(data) ]() mutable { |
| std::shared_ptr<PlatformView> view = self->view_.lock(); |
| if (!view) |
| return; |
| static_cast<PlatformViewAndroid*>(view.get()) |
| ->HandlePlatformMessageResponse(self->response_id_, |
| std::move(data)); |
| })); |
| } |
| |
| void CompleteEmpty() override { |
| fxl::RefPtr<PlatformMessageResponseAndroid> self(this); |
| blink::Threads::Platform()->PostTask(fxl::MakeCopyable([self]() mutable { |
| std::shared_ptr<PlatformView> view = self->view_.lock(); |
| if (!view) |
| return; |
| static_cast<PlatformViewAndroid*>(view.get()) |
| ->HandlePlatformMessageEmptyResponse(self->response_id_); |
| })); |
| } |
| |
| private: |
| PlatformMessageResponseAndroid(int response_id, |
| std::weak_ptr<PlatformView> view) |
| : response_id_(response_id), view_(view) {} |
| |
| int response_id_; |
| std::weak_ptr<PlatformView> view_; |
| }; |
| |
| static std::unique_ptr<AndroidSurface> InitializePlatformSurfaceGL() { |
| const PlatformView::SurfaceConfig offscreen_config = { |
| .red_bits = 8, |
| .green_bits = 8, |
| .blue_bits = 8, |
| .alpha_bits = 8, |
| .depth_bits = 0, |
| .stencil_bits = 0, |
| }; |
| auto surface = std::make_unique<AndroidSurfaceGL>(offscreen_config); |
| return surface->IsOffscreenContextValid() ? std::move(surface) : nullptr; |
| } |
| |
| static std::unique_ptr<AndroidSurface> InitializePlatformSurfaceVulkan() { |
| #if SHELL_ENABLE_VULKAN |
| auto surface = std::make_unique<AndroidSurfaceVulkan>(); |
| return surface->IsValid() ? std::move(surface) : nullptr; |
| #else // SHELL_ENABLE_VULKAN |
| return nullptr; |
| #endif // SHELL_ENABLE_VULKAN |
| } |
| |
| static std::unique_ptr<AndroidSurface> InitializePlatformSurfaceSoftware() { |
| auto surface = std::make_unique<AndroidSurfaceSoftware>(); |
| return surface->IsValid() ? std::move(surface) : nullptr; |
| } |
| |
| static std::unique_ptr<AndroidSurface> InitializePlatformSurface() { |
| if (blink::Settings::Get().enable_software_rendering) { |
| if (auto surface = InitializePlatformSurfaceSoftware()) { |
| FXL_DLOG(INFO) << "Software surface initialized."; |
| return surface; |
| } |
| } |
| |
| if (auto surface = InitializePlatformSurfaceVulkan()) { |
| FXL_DLOG(INFO) << "Vulkan surface initialized."; |
| return surface; |
| } |
| |
| FXL_DLOG(INFO) |
| << "Could not initialize Vulkan surface. Falling back to OpenGL."; |
| |
| if (auto surface = InitializePlatformSurfaceGL()) { |
| FXL_DLOG(INFO) << "GL surface initialized."; |
| return surface; |
| } |
| |
| if (auto surface = InitializePlatformSurfaceSoftware()) { |
| FXL_DLOG(INFO) << "Software surface initialized."; |
| return surface; |
| } |
| |
| FXL_CHECK(false) |
| << "Could not initialize either the Vulkan, OpenGL, or Software" |
| "surface backends. Flutter requires a GPU to render."; |
| return nullptr; |
| } |
| |
| PlatformViewAndroid::PlatformViewAndroid() |
| : PlatformView(std::make_unique<NullRasterizer>()), |
| android_surface_(InitializePlatformSurface()) {} |
| |
| PlatformViewAndroid::~PlatformViewAndroid() = default; |
| |
| void PlatformViewAndroid::Attach() { |
| CreateEngine(); |
| |
| // Eagerly setup the IO thread context. We have already setup the surface. |
| SetupResourceContextOnIOThread(); |
| |
| UpdateThreadPriorities(); |
| } |
| |
| void PlatformViewAndroid::Detach() { |
| ReleaseSurface(); |
| } |
| |
| void PlatformViewAndroid::SurfaceCreated(JNIEnv* env, |
| jobject jsurface, |
| jint backgroundColor) { |
| // 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); |
| |
| // We have a drawing surface, so swap in a non-Null rasterizer. |
| SetRasterizer(std::make_unique<GPURasterizer>(nullptr)); |
| |
| rasterizer_->AddNextFrameCallback([this]() { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| fml::jni::ScopedJavaLocalRef<jobject> view = flutter_view_.get(env); |
| if (!view.is_null()) { |
| FlutterViewOnFirstFrame(env, view.obj()); |
| } |
| }); |
| |
| auto native_window = fxl::MakeRefCounted<AndroidNativeWindow>( |
| ANativeWindow_fromSurface(env, jsurface)); |
| |
| if (!native_window->IsValid()) { |
| return; |
| } |
| |
| if (!android_surface_->SetNativeWindow(native_window)) { |
| return; |
| } |
| |
| std::unique_ptr<Surface> gpu_surface = android_surface_->CreateGPUSurface(); |
| |
| if (gpu_surface == nullptr || !gpu_surface->IsValid()) { |
| return; |
| } |
| |
| NotifyCreated(std::move(gpu_surface), [ |
| this, backgroundColor, native_window_size = native_window->GetSize() |
| ] { rasterizer().Clear(backgroundColor, native_window_size); }); |
| } |
| |
| void PlatformViewAndroid::SurfaceChanged(jint width, jint height) { |
| blink::Threads::Gpu()->PostTask([this, width, height]() { |
| if (android_surface_) { |
| android_surface_->OnScreenSurfaceResize(SkISize::Make(width, height)); |
| } |
| }); |
| } |
| |
| void PlatformViewAndroid::UpdateThreadPriorities() { |
| blink::Threads::Gpu()->PostTask( |
| []() { ::setpriority(PRIO_PROCESS, gettid(), -2); }); |
| |
| blink::Threads::UI()->PostTask( |
| []() { ::setpriority(PRIO_PROCESS, gettid(), -1); }); |
| } |
| |
| void PlatformViewAndroid::SurfaceDestroyed() { |
| ReleaseSurface(); |
| } |
| |
| void PlatformViewAndroid::RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, |
| std::string snapshot_override, |
| std::string entrypoint, |
| bool reuse_runtime_controller, |
| jobject assetManager) { |
| // TODO(jsimmons): remove snapshot_override from the public FlutterView API |
| FXL_CHECK(snapshot_override.empty()) << "snapshot_override is obsolete"; |
| |
| // The flutter assets directory name is the last directory of the bundle_path |
| // and the path into the APK |
| size_t last_slash_idx = bundle_path.rfind("/", bundle_path.size()); |
| std::string flutter_assets_dir = bundle_path.substr( |
| last_slash_idx + 1, bundle_path.size() - last_slash_idx); |
| |
| fxl::RefPtr<blink::AssetProvider> asset_provider = |
| fxl::MakeRefCounted<blink::APKAssetProvider>(env, assetManager, |
| flutter_assets_dir); |
| blink::Threads::UI()->PostTask( |
| [engine = engine_->GetWeakPtr(), |
| asset_provider = std::move(asset_provider), |
| bundle_path = std::move(bundle_path), entrypoint = std::move(entrypoint), |
| reuse_runtime_controller = reuse_runtime_controller] { |
| if (engine) |
| engine->RunBundleWithAssets( |
| std::move(asset_provider), std::move(bundle_path), |
| std::move(entrypoint), reuse_runtime_controller); |
| }); |
| } |
| |
| void PlatformViewAndroid::RunBundleAndSource(std::string bundle_path, |
| std::string main, |
| std::string packages) { |
| blink::Threads::UI()->PostTask([ |
| engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path), |
| main = std::move(main), packages = std::move(packages) |
| ] { |
| if (engine) |
| engine->RunBundleAndSource(std::move(bundle_path), std::move(main), |
| std::move(packages)); |
| }); |
| } |
| |
| void PlatformViewAndroid::SetAssetBundlePathOnUI(std::string bundle_path) { |
| blink::Threads::UI()->PostTask( |
| [ engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path) ] { |
| if (engine) |
| engine->SetAssetBundlePath(std::move(bundle_path)); |
| }); |
| } |
| |
| void PlatformViewAndroid::SetViewportMetrics(jfloat device_pixel_ratio, |
| jint physical_width, |
| jint physical_height, |
| jint physical_padding_top, |
| jint physical_padding_right, |
| jint physical_padding_bottom, |
| jint physical_padding_left, |
| jint physical_view_inset_top, |
| jint physical_view_inset_right, |
| jint physical_view_inset_bottom, |
| jint physical_view_inset_left) { |
| blink::ViewportMetrics metrics; |
| metrics.device_pixel_ratio = device_pixel_ratio; |
| metrics.physical_width = physical_width; |
| metrics.physical_height = physical_height; |
| metrics.physical_padding_top = physical_padding_top; |
| metrics.physical_padding_right = physical_padding_right; |
| metrics.physical_padding_bottom = physical_padding_bottom; |
| metrics.physical_padding_left = physical_padding_left; |
| metrics.physical_view_inset_top = physical_view_inset_top; |
| metrics.physical_view_inset_right = physical_view_inset_right; |
| metrics.physical_view_inset_bottom = physical_view_inset_bottom; |
| metrics.physical_view_inset_left = physical_view_inset_left; |
| |
| blink::Threads::UI()->PostTask([ engine = engine_->GetWeakPtr(), metrics ] { |
| if (engine) |
| engine->SetViewportMetrics(metrics); |
| }); |
| } |
| |
| void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env, |
| std::string name, |
| jobject java_message_data, |
| jint java_message_position, |
| jint response_id) { |
| uint8_t* message_data = |
| static_cast<uint8_t*>(env->GetDirectBufferAddress(java_message_data)); |
| std::vector<uint8_t> message = |
| std::vector<uint8_t>(message_data, message_data + java_message_position); |
| |
| fxl::RefPtr<blink::PlatformMessageResponse> response; |
| if (response_id) { |
| response = fxl::MakeRefCounted<PlatformMessageResponseAndroid>( |
| response_id, GetWeakPtr()); |
| } |
| |
| PlatformView::DispatchPlatformMessage( |
| fxl::MakeRefCounted<blink::PlatformMessage>( |
| std::move(name), std::move(message), std::move(response))); |
| } |
| |
| void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, |
| std::string name, |
| jint response_id) { |
| fxl::RefPtr<blink::PlatformMessageResponse> response; |
| if (response_id) { |
| response = fxl::MakeRefCounted<PlatformMessageResponseAndroid>( |
| response_id, GetWeakPtr()); |
| } |
| |
| PlatformView::DispatchPlatformMessage( |
| fxl::MakeRefCounted<blink::PlatformMessage>(std::move(name), |
| std::move(response))); |
| } |
| |
| void PlatformViewAndroid::DispatchPointerDataPacket(JNIEnv* env, |
| jobject buffer, |
| jint position) { |
| uint8_t* data = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); |
| |
| blink::Threads::UI()->PostTask(fxl::MakeCopyable([ |
| engine = engine_->GetWeakPtr(), |
| packet = std::make_unique<PointerDataPacket>(data, position) |
| ] { |
| if (engine.get()) |
| engine->DispatchPointerDataPacket(*packet); |
| })); |
| } |
| |
| void PlatformViewAndroid::InvokePlatformMessageResponseCallback( |
| JNIEnv* env, |
| jint response_id, |
| jobject java_response_data, |
| jint java_response_position) { |
| if (!response_id) |
| return; |
| auto it = pending_responses_.find(response_id); |
| if (it == pending_responses_.end()) |
| return; |
| uint8_t* response_data = |
| static_cast<uint8_t*>(env->GetDirectBufferAddress(java_response_data)); |
| std::vector<uint8_t> response = std::vector<uint8_t>( |
| response_data, response_data + java_response_position); |
| auto message_response = std::move(it->second); |
| pending_responses_.erase(it); |
| message_response->Complete(std::move(response)); |
| } |
| |
| void PlatformViewAndroid::InvokePlatformMessageEmptyResponseCallback( |
| JNIEnv* env, |
| jint response_id) { |
| if (!response_id) |
| return; |
| auto it = pending_responses_.find(response_id); |
| if (it == pending_responses_.end()) |
| return; |
| auto message_response = std::move(it->second); |
| pending_responses_.erase(it); |
| message_response->CompleteEmpty(); |
| } |
| |
| void PlatformViewAndroid::HandlePlatformMessage( |
| fxl::RefPtr<blink::PlatformMessage> message) { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| fml::jni::ScopedJavaLocalRef<jobject> view = flutter_view_.get(env); |
| if (view.is_null()) |
| return; |
| |
| int response_id = 0; |
| if (auto response = message->response()) { |
| response_id = next_response_id_++; |
| pending_responses_[response_id] = response; |
| } |
| auto java_channel = fml::jni::StringToJavaString(env, message->channel()); |
| if (message->hasData()) { |
| fml::jni::ScopedJavaLocalRef<jbyteArray> message_array( |
| env, env->NewByteArray(message->data().size())); |
| env->SetByteArrayRegion( |
| message_array.obj(), 0, message->data().size(), |
| reinterpret_cast<const jbyte*>(message->data().data())); |
| message = nullptr; |
| |
| // This call can re-enter in InvokePlatformMessageXxxResponseCallback. |
| FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(), |
| message_array.obj(), response_id); |
| } else { |
| message = nullptr; |
| |
| // This call can re-enter in InvokePlatformMessageXxxResponseCallback. |
| FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(), |
| nullptr, response_id); |
| } |
| } |
| |
| void PlatformViewAndroid::HandlePlatformMessageResponse( |
| int response_id, |
| std::vector<uint8_t> data) { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| |
| fml::jni::ScopedJavaLocalRef<jobject> view = flutter_view_.get(env); |
| |
| if (view.is_null()) |
| return; |
| fml::jni::ScopedJavaLocalRef<jbyteArray> data_array( |
| env, env->NewByteArray(data.size())); |
| env->SetByteArrayRegion(data_array.obj(), 0, data.size(), |
| reinterpret_cast<const jbyte*>(data.data())); |
| |
| FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, |
| data_array.obj()); |
| } |
| |
| void PlatformViewAndroid::HandlePlatformMessageEmptyResponse(int response_id) { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| |
| fml::jni::ScopedJavaLocalRef<jobject> view = flutter_view_.get(env); |
| |
| if (view.is_null()) |
| return; |
| FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, |
| nullptr); |
| } |
| |
| void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, |
| jint id, |
| jint action, |
| jobject args, |
| jint args_position) { |
| if (env->IsSameObject(args, NULL)) { |
| std::vector<uint8_t> args_vector; |
| PlatformView::DispatchSemanticsAction( |
| id, static_cast<blink::SemanticsAction>(action), args_vector); |
| return; |
| } |
| |
| uint8_t* args_data = static_cast<uint8_t*>(env->GetDirectBufferAddress(args)); |
| std::vector<uint8_t> args_vector = |
| std::vector<uint8_t>(args_data, args_data + args_position); |
| |
| PlatformView::DispatchSemanticsAction( |
| id, static_cast<blink::SemanticsAction>(action), std::move(args_vector)); |
| } |
| |
| void PlatformViewAndroid::SetSemanticsEnabled(jboolean enabled) { |
| PlatformView::SetSemanticsEnabled(enabled); |
| } |
| |
| void PlatformViewAndroid::ReleaseSurface() { |
| NotifyDestroyed(); |
| android_surface_->TeardownOnScreenContext(); |
| SetRasterizer(std::make_unique<NullRasterizer>()); |
| } |
| |
| VsyncWaiter* PlatformViewAndroid::GetVsyncWaiter() { |
| if (!vsync_waiter_) |
| vsync_waiter_ = std::make_unique<VsyncWaiterAndroid>(); |
| return vsync_waiter_.get(); |
| } |
| |
| bool PlatformViewAndroid::ResourceContextMakeCurrent() { |
| FXL_CHECK(android_surface_); |
| return android_surface_->ResourceContextMakeCurrent(); |
| } |
| |
| void PlatformViewAndroid::UpdateSemantics( |
| blink::SemanticsNodeUpdates update) { |
| constexpr size_t kBytesPerNode = 36 * sizeof(int32_t); |
| constexpr size_t kBytesPerChild = sizeof(int32_t); |
| |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| { |
| fml::jni::ScopedJavaLocalRef<jobject> view = flutter_view_.get(env); |
| if (view.is_null()) |
| return; |
| |
| size_t num_bytes = 0; |
| for (const auto& value : update) { |
| num_bytes += kBytesPerNode; |
| num_bytes += value.second.children.size() * kBytesPerChild; |
| } |
| |
| std::vector<uint8_t> buffer(num_bytes); |
| int32_t* buffer_int32 = reinterpret_cast<int32_t*>(&buffer[0]); |
| float* buffer_float32 = reinterpret_cast<float*>(&buffer[0]); |
| |
| std::vector<std::string> strings; |
| size_t position = 0; |
| for (const auto& value : update) { |
| // If you edit this code, make sure you update kBytesPerNode |
| // and/or kBytesPerChild above to match the number of values you are |
| // sending. |
| const blink::SemanticsNode& node = value.second; |
| buffer_int32[position++] = node.id; |
| buffer_int32[position++] = node.flags; |
| buffer_int32[position++] = node.actions; |
| buffer_int32[position++] = node.textSelectionBase; |
| buffer_int32[position++] = node.textSelectionExtent; |
| buffer_float32[position++] = (float)node.scrollPosition; |
| buffer_float32[position++] = (float)node.scrollExtentMax; |
| buffer_float32[position++] = (float)node.scrollExtentMin; |
| if (node.label.empty()) { |
| buffer_int32[position++] = -1; |
| } else { |
| buffer_int32[position++] = strings.size(); |
| strings.push_back(node.label); |
| } |
| if (node.value.empty()) { |
| buffer_int32[position++] = -1; |
| } else { |
| buffer_int32[position++] = strings.size(); |
| strings.push_back(node.value); |
| } |
| if (node.increasedValue.empty()) { |
| buffer_int32[position++] = -1; |
| } else { |
| buffer_int32[position++] = strings.size(); |
| strings.push_back(node.increasedValue); |
| } |
| if (node.decreasedValue.empty()) { |
| buffer_int32[position++] = -1; |
| } else { |
| buffer_int32[position++] = strings.size(); |
| strings.push_back(node.decreasedValue); |
| } |
| if (node.hint.empty()) { |
| buffer_int32[position++] = -1; |
| } else { |
| buffer_int32[position++] = strings.size(); |
| strings.push_back(node.hint); |
| } |
| buffer_int32[position++] = node.textDirection; |
| buffer_int32[position++] = node.previousNodeId; |
| buffer_float32[position++] = node.rect.left(); |
| buffer_float32[position++] = node.rect.top(); |
| buffer_float32[position++] = node.rect.right(); |
| buffer_float32[position++] = node.rect.bottom(); |
| node.transform.asColMajorf(&buffer_float32[position]); |
| position += 16; |
| buffer_int32[position++] = node.children.size(); |
| for (int32_t child : node.children) |
| buffer_int32[position++] = child; |
| } |
| |
| fml::jni::ScopedJavaLocalRef<jobject> direct_buffer( |
| env, env->NewDirectByteBuffer(buffer.data(), buffer.size())); |
| |
| FlutterViewUpdateSemantics( |
| env, view.obj(), direct_buffer.obj(), |
| fml::jni::VectorToStringArray(env, strings).obj()); |
| } |
| } |
| |
| void PlatformViewAndroid::RunFromSource(const std::string& assets_directory, |
| const std::string& main, |
| const std::string& packages) { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| FXL_CHECK(env); |
| |
| { |
| fml::jni::ScopedJavaLocalRef<jobject> local_flutter_view = |
| flutter_view_.get(env); |
| if (local_flutter_view.is_null()) { |
| // Collected. |
| return; |
| } |
| |
| // Grab the class of the flutter view. |
| jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); |
| FXL_CHECK(flutter_view_class); |
| |
| // Grab the runFromSource method id. |
| jmethodID run_from_source_method_id = env->GetMethodID( |
| flutter_view_class, "runFromSource", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); |
| FXL_CHECK(run_from_source_method_id); |
| |
| // Invoke runFromSource on the Android UI thread. |
| jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); |
| FXL_CHECK(java_assets_directory); |
| jstring java_main = env->NewStringUTF(main.c_str()); |
| FXL_CHECK(java_main); |
| jstring java_packages = env->NewStringUTF(packages.c_str()); |
| FXL_CHECK(java_packages); |
| env->CallVoidMethod(local_flutter_view.obj(), run_from_source_method_id, |
| java_assets_directory, java_main, java_packages); |
| } |
| |
| // Detaching from the VM deletes any stray local references. |
| fml::jni::DetachFromVM(); |
| } |
| |
| void PlatformViewAndroid::SetAssetBundlePath( |
| const std::string& assets_directory) { |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| FXL_CHECK(env); |
| |
| { |
| fml::jni::ScopedJavaLocalRef<jobject> local_flutter_view = |
| flutter_view_.get(env); |
| if (local_flutter_view.is_null()) { |
| // Collected. |
| return; |
| } |
| |
| // Grab the class of the flutter view. |
| jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); |
| FXL_CHECK(flutter_view_class); |
| |
| // Grab the setAssetBundlePath method id. |
| jmethodID method_id = env->GetMethodID( |
| flutter_view_class, "setAssetBundlePathOnUI", "(Ljava/lang/String;)V"); |
| FXL_CHECK(method_id); |
| |
| // Invoke setAssetBundlePath on the Android UI thread. |
| jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); |
| FXL_CHECK(java_assets_directory); |
| |
| env->CallVoidMethod(local_flutter_view.obj(), method_id, |
| java_assets_directory); |
| } |
| |
| // Detaching from the VM deletes any stray local references. |
| fml::jni::DetachFromVM(); |
| } |
| |
| void PlatformViewAndroid::RegisterExternalTexture( |
| int64_t texture_id, |
| const fml::jni::JavaObjectWeakGlobalRef& surface_texture) { |
| RegisterTexture( |
| std::make_shared<AndroidExternalTextureGL>(texture_id, surface_texture)); |
| } |
| |
| void PlatformViewAndroid::MarkTextureFrameAvailable(int64_t texture_id) { |
| blink::Threads::Gpu()->PostTask([this, texture_id]() { |
| std::shared_ptr<AndroidExternalTextureGL> texture = |
| static_pointer_cast<AndroidExternalTextureGL>( |
| rasterizer_->GetTextureRegistry().GetTexture(texture_id)); |
| if (texture) { |
| texture->MarkNewFrameAvailable(); |
| } |
| }); |
| PlatformView::MarkTextureFrameAvailable(texture_id); |
| } |
| |
| fml::jni::ScopedJavaLocalRef<jobject> PlatformViewAndroid::GetBitmap( |
| JNIEnv* env) { |
| // Render the last frame to an array of pixels on the GPU thread. |
| // The pixels will be returned as a global JNI reference to an int array. |
| fxl::AutoResetWaitableEvent latch; |
| jobject pixels_ref = nullptr; |
| SkISize frame_size; |
| blink::Threads::Gpu()->PostTask([this, &latch, &pixels_ref, &frame_size]() { |
| GetBitmapGpuTask(&pixels_ref, &frame_size); |
| latch.Signal(); |
| }); |
| |
| latch.Wait(); |
| |
| // Convert the pixel array to an Android bitmap. |
| if (pixels_ref == nullptr) |
| return fml::jni::ScopedJavaLocalRef<jobject>(); |
| |
| fml::jni::ScopedJavaGlobalRef<jobject> pixels(env, pixels_ref); |
| |
| jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); |
| FXL_CHECK(bitmap_class); |
| |
| jmethodID create_bitmap = env->GetStaticMethodID( |
| bitmap_class, "createBitmap", |
| "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); |
| FXL_CHECK(create_bitmap); |
| |
| jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); |
| FXL_CHECK(bitmap_config_class); |
| |
| jmethodID bitmap_config_value_of = env->GetStaticMethodID( |
| bitmap_config_class, "valueOf", |
| "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); |
| FXL_CHECK(bitmap_config_value_of); |
| |
| jstring argb = env->NewStringUTF("ARGB_8888"); |
| FXL_CHECK(argb); |
| |
| jobject bitmap_config = env->CallStaticObjectMethod( |
| bitmap_config_class, bitmap_config_value_of, argb); |
| FXL_CHECK(bitmap_config); |
| |
| jobject bitmap = env->CallStaticObjectMethod( |
| bitmap_class, create_bitmap, pixels.obj(), frame_size.width(), |
| frame_size.height(), bitmap_config); |
| |
| return fml::jni::ScopedJavaLocalRef<jobject>(env, bitmap); |
| } |
| |
| void PlatformViewAndroid::GetBitmapGpuTask(jobject* pixels_out, |
| SkISize* size_out) { |
| flow::LayerTree* layer_tree = rasterizer_->GetLastLayerTree(); |
| if (layer_tree == nullptr) |
| return; |
| |
| JNIEnv* env = fml::jni::AttachCurrentThread(); |
| FXL_CHECK(env); |
| |
| const SkISize& frame_size = layer_tree->frame_size(); |
| jsize pixels_size = frame_size.width() * frame_size.height(); |
| jintArray pixels_array = env->NewIntArray(pixels_size); |
| FXL_CHECK(pixels_array); |
| |
| jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); |
| FXL_CHECK(pixels); |
| |
| SkImageInfo image_info = |
| SkImageInfo::Make(frame_size.width(), frame_size.height(), |
| kRGBA_8888_SkColorType, kPremul_SkAlphaType); |
| |
| sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect( |
| image_info, pixels, frame_size.width() * sizeof(jint)); |
| |
| flow::CompositorContext compositor_context(nullptr); |
| SkCanvas* canvas = surface->getCanvas(); |
| flow::CompositorContext::ScopedFrame frame = |
| compositor_context.AcquireFrame(nullptr, canvas, false); |
| |
| canvas->clear(SK_ColorBLACK); |
| layer_tree->Raster(frame); |
| canvas->flush(); |
| |
| // 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++) { |
| uint8_t* bytes = reinterpret_cast<uint8_t*>(pixels + i); |
| std::swap(bytes[0], bytes[2]); |
| } |
| |
| env->ReleaseIntArrayElements(pixels_array, pixels, 0); |
| |
| *pixels_out = env->NewGlobalRef(pixels_array); |
| *size_out = frame_size; |
| |
| fml::jni::DetachFromVM(); |
| } |
| |
| } // namespace shell |