blob: 79605a574345f27a2f7fc35e7890b0ac80f96091 [file] [log] [blame]
// 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.
#define FML_USED_ON_EMBEDDER
#include "flutter/shell/platform/android/android_shell_holder.h"
#include <pthread.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <utility>
#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/lib/ui/painting/image_generator_registry.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/platform/android/android_display.h"
#include "flutter/shell/platform/android/android_image_generator.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/platform_view_android.h"
namespace flutter {
/// Inheriting ThreadConfigurer and use Android platform thread API to configure
/// the thread priorities
static void AndroidPlatformThreadConfigSetter(
const fml::Thread::ThreadConfig& config) {
// set thread name
fml::Thread::SetCurrentThreadName(config);
// set thread priority
switch (config.priority) {
case fml::Thread::ThreadPriority::BACKGROUND: {
if (::setpriority(PRIO_PROCESS, 0, 10) != 0) {
FML_LOG(ERROR) << "Failed to set IO task runner priority";
}
break;
}
case fml::Thread::ThreadPriority::DISPLAY: {
if (::setpriority(PRIO_PROCESS, 0, -1) != 0) {
FML_LOG(ERROR) << "Failed to set UI task runner priority";
}
break;
}
case fml::Thread::ThreadPriority::RASTER: {
// Android describes -8 as "most important display threads, for
// compositing the screen and retrieving input events". Conservatively
// set the raster thread to slightly lower priority than it.
if (::setpriority(PRIO_PROCESS, 0, -5) != 0) {
// Defensive fallback. Depending on the OEM, it may not be possible
// to set priority to -5.
if (::setpriority(PRIO_PROCESS, 0, -2) != 0) {
FML_LOG(ERROR) << "Failed to set raster task runner priority";
}
}
break;
}
default:
if (::setpriority(PRIO_PROCESS, 0, 0) != 0) {
FML_LOG(ERROR) << "Failed to set priority";
}
}
}
static PlatformData GetDefaultPlatformData() {
PlatformData platform_data;
platform_data.lifecycle_state = "AppLifecycleState.detached";
return platform_data;
}
AndroidShellHolder::AndroidShellHolder(
const flutter::Settings& settings,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: settings_(settings), jni_facade_(jni_facade) {
static size_t thread_host_count = 1;
auto thread_label = std::to_string(thread_host_count++);
auto mask =
ThreadHost::Type::UI | ThreadHost::Type::RASTER | ThreadHost::Type::IO;
flutter::ThreadHost::ThreadHostConfig host_config(
thread_label, mask, AndroidPlatformThreadConfigSetter);
host_config.ui_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::UI, thread_label),
fml::Thread::ThreadPriority::DISPLAY);
host_config.raster_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::RASTER, thread_label),
fml::Thread::ThreadPriority::RASTER);
host_config.io_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::IO, thread_label),
fml::Thread::ThreadPriority::NORMAL);
thread_host_ = std::make_shared<ThreadHost>(host_config);
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
[&jni_facade, &weak_platform_view](Shell& shell) {
std::unique_ptr<PlatformViewAndroid> platform_view_android;
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
jni_facade, // JNI interop
shell.GetSettings()
.enable_software_rendering, // use software rendering
shell.GetSettings().msaa_samples // msaa sample count
);
weak_platform_view = platform_view_android->GetWeakPtr();
std::vector<std::unique_ptr<Display>> displays;
displays.push_back(std::make_unique<AndroidDisplay>(jni_facade));
shell.OnDisplayUpdates(DisplayUpdateType::kStartup,
std::move(displays));
return platform_view_android;
};
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell);
};
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr<fml::TaskRunner> raster_runner;
fml::RefPtr<fml::TaskRunner> ui_runner;
fml::RefPtr<fml::TaskRunner> io_runner;
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
raster_runner = thread_host_->raster_thread->GetTaskRunner();
ui_runner = thread_host_->ui_thread->GetTaskRunner();
io_runner = thread_host_->io_thread->GetTaskRunner();
flutter::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
raster_runner, // raster
ui_runner, // ui
io_runner // io
);
shell_ =
Shell::Create(GetDefaultPlatformData(), // window data
task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
if (shell_) {
shell_->GetDartVM()->GetConcurrentMessageLoop()->PostTaskToAllWorkers([]() {
if (::setpriority(PRIO_PROCESS, gettid(), 1) != 0) {
FML_LOG(ERROR) << "Failed to set Workers task runner priority";
}
});
shell_->RegisterImageDecoder(
[runner = task_runners.GetIOTaskRunner()](sk_sp<SkData> buffer) {
return AndroidImageGenerator::MakeFromData(std::move(buffer), runner);
},
-1);
FML_DLOG(INFO) << "Registered Android SDK image decoder (API level 28+)";
}
platform_view_ = weak_platform_view;
FML_DCHECK(platform_view_);
is_valid_ = shell_ != nullptr;
}
AndroidShellHolder::AndroidShellHolder(
const Settings& settings,
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade,
const std::shared_ptr<ThreadHost>& thread_host,
std::unique_ptr<Shell> shell,
const fml::WeakPtr<PlatformViewAndroid>& platform_view)
: settings_(settings),
jni_facade_(jni_facade),
platform_view_(platform_view),
thread_host_(thread_host),
shell_(std::move(shell)) {
FML_DCHECK(jni_facade);
FML_DCHECK(shell_);
FML_DCHECK(shell_->IsSetup());
FML_DCHECK(platform_view_);
FML_DCHECK(thread_host_);
is_valid_ = shell_ != nullptr;
}
AndroidShellHolder::~AndroidShellHolder() {
shell_.reset();
thread_host_.reset();
}
bool AndroidShellHolder::IsValid() const {
return is_valid_;
}
const flutter::Settings& AndroidShellHolder::GetSettings() const {
return settings_;
}
std::unique_ptr<AndroidShellHolder> AndroidShellHolder::Spawn(
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
const std::string& entrypoint,
const std::string& libraryUrl,
const std::string& initial_route,
const std::vector<std::string>& entrypoint_args) const {
FML_DCHECK(shell_ && shell_->IsSetup())
<< "A new Shell can only be spawned "
"if the current Shell is properly constructed";
// Pull out the new PlatformViewAndroid from the new Shell to feed to it to
// the new AndroidShellHolder.
//
// It's a weak pointer because it's owned by the Shell (which we're also)
// making below. And the AndroidShellHolder then owns the Shell.
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
// Take out the old AndroidContext to reuse inside the PlatformViewAndroid
// of the new Shell.
PlatformViewAndroid* android_platform_view = platform_view_.get();
// There's some indirection with platform_view_ being a weak pointer but
// we just checked that the shell_ exists above and a valid shell is the
// owner of the platform view so this weak pointer always exists.
FML_DCHECK(android_platform_view);
std::shared_ptr<flutter::AndroidContext> android_context =
android_platform_view->GetAndroidContext();
FML_DCHECK(android_context);
// This is a synchronous call, so the captures don't have race checks.
Shell::CreateCallback<PlatformView> on_create_platform_view =
[&jni_facade, android_context, &weak_platform_view](Shell& shell) {
std::unique_ptr<PlatformViewAndroid> platform_view_android;
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
jni_facade, // JNI interop
android_context // Android context
);
weak_platform_view = platform_view_android->GetWeakPtr();
std::vector<std::unique_ptr<Display>> displays;
displays.push_back(std::make_unique<AndroidDisplay>(jni_facade));
shell.OnDisplayUpdates(DisplayUpdateType::kStartup,
std::move(displays));
return platform_view_android;
};
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell);
};
// TODO(xster): could be worth tracing this to investigate whether
// the IsolateConfiguration could be cached somewhere.
auto config = BuildRunConfiguration(entrypoint, libraryUrl, entrypoint_args);
if (!config) {
// If the RunConfiguration was null, the kernel blob wasn't readable.
// Fail the whole thing.
return nullptr;
}
std::unique_ptr<flutter::Shell> shell =
shell_->Spawn(std::move(config.value()), initial_route,
on_create_platform_view, on_create_rasterizer);
return std::unique_ptr<AndroidShellHolder>(
new AndroidShellHolder(GetSettings(), jni_facade, thread_host_,
std::move(shell), weak_platform_view));
}
void AndroidShellHolder::Launch(
std::unique_ptr<APKAssetProvider> apk_asset_provider,
const std::string& entrypoint,
const std::string& libraryUrl,
const std::vector<std::string>& entrypoint_args) {
if (!IsValid()) {
return;
}
apk_asset_provider_ = std::move(apk_asset_provider);
auto config = BuildRunConfiguration(entrypoint, libraryUrl, entrypoint_args);
if (!config) {
return;
}
shell_->RunEngine(std::move(config.value()));
}
Rasterizer::Screenshot AndroidShellHolder::Screenshot(
Rasterizer::ScreenshotType type,
bool base64_encode) {
if (!IsValid()) {
return {nullptr, SkISize::MakeEmpty()};
}
return shell_->Screenshot(type, base64_encode);
}
fml::WeakPtr<PlatformViewAndroid> AndroidShellHolder::GetPlatformView() {
FML_DCHECK(platform_view_);
return platform_view_;
}
void AndroidShellHolder::NotifyLowMemoryWarning() {
FML_DCHECK(shell_);
shell_->NotifyLowMemoryWarning();
}
std::optional<RunConfiguration> AndroidShellHolder::BuildRunConfiguration(
const std::string& entrypoint,
const std::string& libraryUrl,
const std::vector<std::string>& entrypoint_args) const {
std::unique_ptr<IsolateConfiguration> isolate_configuration;
if (flutter::DartVM::IsRunningPrecompiledCode()) {
isolate_configuration = IsolateConfiguration::CreateForAppSnapshot();
} else {
std::unique_ptr<fml::Mapping> kernel_blob =
fml::FileMapping::CreateReadOnly(
GetSettings().application_kernel_asset);
if (!kernel_blob) {
FML_DLOG(ERROR) << "Unable to load the kernel blob asset.";
return std::nullopt;
}
isolate_configuration =
IsolateConfiguration::CreateForKernel(std::move(kernel_blob));
}
RunConfiguration config(std::move(isolate_configuration));
config.AddAssetResolver(apk_asset_provider_->Clone());
{
if (!entrypoint.empty() && !libraryUrl.empty()) {
config.SetEntrypointAndLibrary(entrypoint, libraryUrl);
} else if (!entrypoint.empty()) {
config.SetEntrypoint(entrypoint);
}
if (!entrypoint_args.empty()) {
config.SetEntrypointArgs(entrypoint_args);
}
}
return config;
}
} // namespace flutter