blob: 929b00fdab7c9451970105533427247775a14add [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/flutter_main.h"
#include <android/log.h>
#include <optional>
#include <vector>
#include "flutter/fml/command_line.h"
#include "flutter/fml/file.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/fml/platform/android/paths_android.h"
#include "flutter/fml/size.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/shell/common/shell.h"
#include "flutter/shell/common/switches.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
#include "third_party/skia/include/core/SkFontMgr.h"
namespace flutter {
extern "C" {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Used for debugging dart:* sources.
extern const uint8_t kPlatformStrongDill[];
extern const intptr_t kPlatformStrongDillSize;
#endif
}
namespace {
// This is only available on API 23+, so dynamically look it up.
// This method is only called once at shell creation.
// Do this in C++ because the API is available at level 23 here, but only 29+ in
// Java.
bool IsATraceEnabled() {
auto libandroid = fml::NativeLibrary::Create("libandroid.so");
FML_CHECK(libandroid);
auto atrace_fn =
libandroid->ResolveFunction<bool (*)(void)>("ATrace_isEnabled");
if (atrace_fn) {
return atrace_fn.value()();
}
return false;
}
fml::jni::ScopedJavaGlobalRef<jclass>* g_flutter_jni_class = nullptr;
} // anonymous namespace
FlutterMain::FlutterMain(flutter::Settings settings)
: settings_(std::move(settings)), observatory_uri_callback_() {}
FlutterMain::~FlutterMain() = default;
static std::unique_ptr<FlutterMain> g_flutter_main;
FlutterMain& FlutterMain::Get() {
FML_CHECK(g_flutter_main) << "ensureInitializationComplete must have already "
"been called.";
return *g_flutter_main;
}
const flutter::Settings& FlutterMain::GetSettings() const {
return settings_;
}
void FlutterMain::Init(JNIEnv* env,
jclass clazz,
jobject context,
jobjectArray jargs,
jstring kernelPath,
jstring appStoragePath,
jstring engineCachesPath,
jlong initTimeMillis) {
std::vector<std::string> args;
args.push_back("flutter");
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
args.push_back(std::move(arg));
}
auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());
auto settings = SettingsFromCommandLine(command_line);
// Turn systracing on if ATrace_isEnabled is true and the user did not already
// request systracing
if (!settings.trace_systrace) {
settings.trace_systrace = IsATraceEnabled();
if (settings.trace_systrace) {
__android_log_print(
ANDROID_LOG_INFO, "Flutter",
"ATrace was enabled at startup. Flutter and Dart "
"tracing will be forwarded to systrace and will not show up in the "
"Observatory timeline or Dart DevTools.");
}
}
#if FLUTTER_RELEASE
// On most platforms the timeline is always disabled in release mode.
// On Android, enable it in release mode only when using systrace.
settings.enable_timeline_event_handler = settings.trace_systrace;
#endif // FLUTTER_RELEASE
int64_t init_time_micros = initTimeMillis * 1000;
settings.engine_start_timestamp =
std::chrono::microseconds(Dart_TimelineGetMicros() - init_time_micros);
// Restore the callback cache.
// TODO(chinmaygarde): Route all cache file access through FML and remove this
// setter.
flutter::DartCallbackCache::SetCachePath(
fml::jni::JavaStringToString(env, appStoragePath));
fml::paths::InitializeAndroidCachesPath(
fml::jni::JavaStringToString(env, engineCachesPath));
flutter::DartCallbackCache::LoadCacheFromDisk();
if (!flutter::DartVM::IsRunningPrecompiledCode() && kernelPath) {
// Check to see if the appropriate kernel files are present and configure
// settings accordingly.
auto application_kernel_path =
fml::jni::JavaStringToString(env, kernelPath);
if (fml::IsFile(application_kernel_path)) {
settings.application_kernel_asset = application_kernel_path;
}
}
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
settings.log_message_callback = [](const std::string& tag,
const std::string& message) {
__android_log_print(ANDROID_LOG_INFO, tag.c_str(), "%.*s",
(int)message.size(), message.c_str());
};
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
return [mapping, size]() {
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
};
};
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Not thread safe. Will be removed when FlutterMain is refactored to no
// longer be a singleton.
g_flutter_main.reset(new FlutterMain(std::move(settings)));
g_flutter_main->SetupObservatoryUriCallback(env);
}
void FlutterMain::SetupObservatoryUriCallback(JNIEnv* env) {
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
if (g_flutter_jni_class->is_null()) {
return;
}
jfieldID uri_field = env->GetStaticFieldID(
g_flutter_jni_class->obj(), "observatoryUri", "Ljava/lang/String;");
if (uri_field == nullptr) {
return;
}
auto set_uri = [env, uri_field](std::string uri) {
fml::jni::ScopedJavaLocalRef<jstring> java_uri =
fml::jni::StringToJavaString(env, uri);
env->SetStaticObjectField(g_flutter_jni_class->obj(), uri_field,
java_uri.obj());
};
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
observatory_uri_callback_ = DartServiceIsolate::AddServerStatusCallback(
[platform_runner, set_uri](std::string uri) {
platform_runner->PostTask([uri, set_uri] { set_uri(uri); });
});
}
static void PrefetchDefaultFontManager(JNIEnv* env, jclass jcaller) {
// Initialize a singleton owned by Skia.
SkFontMgr::RefDefault();
}
bool FlutterMain::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{
.name = "nativeInit",
.signature = "(Landroid/content/Context;[Ljava/lang/String;Ljava/"
"lang/String;Ljava/lang/String;Ljava/lang/String;J)V",
.fnPtr = reinterpret_cast<void*>(&Init),
},
{
.name = "nativePrefetchDefaultFontManager",
.signature = "()V",
.fnPtr = reinterpret_cast<void*>(&PrefetchDefaultFontManager),
},
};
jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
if (clazz == nullptr) {
return false;
}
return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0;
}
} // namespace flutter