// 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 #include #include "flutter/shell/common/io_manager.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/platform_message_response_android.h" #include "flutter/shell/platform/android/platform_view_android_jni.h" #include "flutter/shell/platform/android/vsync_waiter_android.h" #include "lib/fxl/synchronization/waitable_event.h" namespace shell { PlatformViewAndroid::PlatformViewAndroid( PlatformView::Delegate& delegate, blink::TaskRunners task_runners, fml::jni::JavaObjectWeakGlobalRef java_object, bool use_software_rendering) : PlatformView(delegate, std::move(task_runners)), java_object_(java_object), android_surface_(AndroidSurface::Create(use_software_rendering)) { FXL_CHECK(android_surface_) << "Could not create an OpenGL, Vulkan or Software surface to setup " "rendering."; } PlatformViewAndroid::~PlatformViewAndroid() = default; void PlatformViewAndroid::NotifyCreated( fxl::RefPtr native_window) { InstallFirstFrameCallback(); android_surface_->SetNativeWindow(native_window); PlatformView::NotifyCreated(); } void PlatformViewAndroid::NotifyDestroyed() { PlatformView::NotifyDestroyed(); android_surface_->TeardownOnScreenContext(); } void PlatformViewAndroid::NotifyChanged(const SkISize& size) { fxl::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetGPUTaskRunner(), // [&latch, surface = android_surface_.get(), size]() { surface->OnScreenSurfaceResize(size); latch.Signal(); }); latch.Wait(); } 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(env->GetDirectBufferAddress(java_message_data)); std::vector message = std::vector(message_data, message_data + java_message_position); fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( fxl::MakeRefCounted( std::move(name), std::move(message), std::move(response))); } void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, std::string name, jint response_id) { fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( fxl::MakeRefCounted(std::move(name), std::move(response))); } 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(env->GetDirectBufferAddress(java_response_data)); std::vector response = std::vector( 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(); } // |shell::PlatformView| void PlatformViewAndroid::HandlePlatformMessage( fxl::RefPtr message) { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef view = java_object_.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 message_array( env, env->NewByteArray(message->data().size())); env->SetByteArrayRegion( message_array.obj(), 0, message->data().size(), reinterpret_cast(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::DispatchSemanticsAction(JNIEnv* env, jint id, jint action, jobject args, jint args_position) { if (env->IsSameObject(args, NULL)) { std::vector args_vector; PlatformView::DispatchSemanticsAction( id, static_cast(action), args_vector); return; } uint8_t* args_data = static_cast(env->GetDirectBufferAddress(args)); std::vector args_vector = std::vector(args_data, args_data + args_position); PlatformView::DispatchSemanticsAction( id, static_cast(action), std::move(args_vector)); } // |shell::PlatformView| void PlatformViewAndroid::UpdateSemantics(blink::SemanticsNodeUpdates update) { constexpr size_t kBytesPerNode = 35 * sizeof(int32_t); constexpr size_t kBytesPerChild = sizeof(int32_t); JNIEnv* env = fml::jni::AttachCurrentThread(); { fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) return; size_t num_bytes = 0; for (const auto& value : update) { num_bytes += kBytesPerNode; num_bytes += value.second.childrenInTraversalOrder.size() * kBytesPerChild; num_bytes += value.second.childrenInHitTestOrder.size() * kBytesPerChild; } std::vector buffer(num_bytes); int32_t* buffer_int32 = reinterpret_cast(&buffer[0]); float* buffer_float32 = reinterpret_cast(&buffer[0]); std::vector 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_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.childrenInTraversalOrder.size(); for (int32_t child : node.childrenInTraversalOrder) buffer_int32[position++] = child; for (int32_t child : node.childrenInHitTestOrder) buffer_int32[position++] = child; } fml::jni::ScopedJavaLocalRef direct_buffer( env, env->NewDirectByteBuffer(buffer.data(), buffer.size())); FlutterViewUpdateSemantics( env, view.obj(), direct_buffer.obj(), fml::jni::VectorToStringArray(env, strings).obj()); } } void PlatformViewAndroid::RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture) { RegisterTexture( std::make_shared(texture_id, surface_texture)); } // |shell::PlatformView| std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { return std::make_unique(task_runners_); } // |shell::PlatformView| std::unique_ptr PlatformViewAndroid::CreateRenderingSurface() { return android_surface_->CreateGPUSurface(); } // |shell::PlatformView| sk_sp PlatformViewAndroid::CreateResourceContext() const { sk_sp resource_context; if (android_surface_->ResourceContextMakeCurrent()) { // TODO(chinmaygarde): Currently, this code depends on the fact that only // the OpenGL surface will be able to make a resource context current. If // this changes, this assumption breaks. Handle the same. resource_context = IOManager::CreateCompatibleResourceLoadingContext( GrBackend::kOpenGL_GrBackend); } else { FXL_DLOG(ERROR) << "Could not make the resource context current."; } return resource_context; } void PlatformViewAndroid::InstallFirstFrameCallback() { // On Platform Task Runner. SetNextFrameCallback( [platform_view = GetWeakPtr(), platform_task_runner = task_runners_.GetPlatformTaskRunner()]() { // On GPU Task Runner. platform_task_runner->PostTask([platform_view]() { // Back on Platform Task Runner. if (platform_view) { reinterpret_cast(platform_view.get()) ->FireFirstFrameCallback(); } }); }); } void PlatformViewAndroid::FireFirstFrameCallback() { JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) { // The Java object died. return; } FlutterViewOnFirstFrame(fml::jni::AttachCurrentThread(), view.obj()); } } // namespace shell