blob: 5c7ea0279a4a7bc4e0496f9caa895628f52e1855 [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 RAPIDJSON_HAS_STDSTRING 1
#include "flutter/runtime/service_protocol.h"
#include <string.h>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "flutter/fml/posix_wrappers.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
namespace flutter {
const std::string_view ServiceProtocol::kScreenshotExtensionName =
"_flutter.screenshot";
const std::string_view ServiceProtocol::kScreenshotSkpExtensionName =
"_flutter.screenshotSkp";
const std::string_view ServiceProtocol::kRunInViewExtensionName =
"_flutter.runInView";
const std::string_view ServiceProtocol::kFlushUIThreadTasksExtensionName =
"_flutter.flushUIThreadTasks";
const std::string_view ServiceProtocol::kSetAssetBundlePathExtensionName =
"_flutter.setAssetBundlePath";
const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName =
"_flutter.getDisplayRefreshRate";
const std::string_view ServiceProtocol::kGetSkSLsExtensionName =
"_flutter.getSkSLs";
static constexpr std::string_view kViewIdPrefx = "_flutterView/";
static constexpr std::string_view kListViewsExtensionName =
"_flutter.listViews";
ServiceProtocol::ServiceProtocol()
: endpoints_({
// Private
kListViewsExtensionName,
// Public
kScreenshotExtensionName,
kScreenshotSkpExtensionName,
kRunInViewExtensionName,
kFlushUIThreadTasksExtensionName,
kSetAssetBundlePathExtensionName,
kGetDisplayRefreshRateExtensionName,
kGetSkSLsExtensionName,
}),
handlers_mutex_(fml::SharedMutex::Create()) {}
ServiceProtocol::~ServiceProtocol() {
ToggleHooks(false);
}
void ServiceProtocol::AddHandler(Handler* handler,
Handler::Description description) {
fml::UniqueLock lock(*handlers_mutex_);
handlers_.emplace(handler, description);
}
void ServiceProtocol::RemoveHandler(Handler* handler) {
fml::UniqueLock lock(*handlers_mutex_);
handlers_.erase(handler);
}
void ServiceProtocol::SetHandlerDescription(Handler* handler,
Handler::Description description) {
fml::SharedLock lock(*handlers_mutex_);
auto it = handlers_.find(handler);
if (it != handlers_.end())
it->second.Store(description);
}
void ServiceProtocol::ToggleHooks(bool set) {
for (const auto& endpoint : endpoints_) {
Dart_RegisterIsolateServiceRequestCallback(
endpoint.data(), // method
&ServiceProtocol::HandleMessage, // callback
set ? this : nullptr // user data
);
}
}
static void WriteServerErrorResponse(rapidjson::Document& document,
const char* message) {
document.SetObject();
document.AddMember("code", -32000, document.GetAllocator());
rapidjson::Value message_value;
message_value.SetString(message, document.GetAllocator());
document.AddMember("message", message_value, document.GetAllocator());
}
bool ServiceProtocol::HandleMessage(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** json_object) {
Handler::ServiceProtocolMap params;
for (intptr_t i = 0; i < num_params; i++) {
params[std::string_view{param_keys[i]}] = std::string_view{param_values[i]};
}
#ifndef NDEBUG
FML_DLOG(INFO) << "Service protcol method: " << method;
FML_DLOG(INFO) << "Arguments: " << params.size();
for (intptr_t i = 0; i < num_params; i++) {
FML_DLOG(INFO) << " " << i + 1 << ": " << param_keys[i] << " = "
<< param_values[i];
}
#endif // NDEBUG
rapidjson::Document document;
bool result = HandleMessage(std::string_view{method}, //
params, //
static_cast<ServiceProtocol*>(user_data), //
document //
);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
*json_object = fml::strdup(buffer.GetString());
#ifndef NDEBUG
FML_DLOG(INFO) << "Response: " << *json_object;
FML_DLOG(INFO) << "RPC Result: " << result;
#endif // NDEBUG
return result;
}
bool ServiceProtocol::HandleMessage(std::string_view method,
const Handler::ServiceProtocolMap& params,
ServiceProtocol* service_protocol,
rapidjson::Document& response) {
if (service_protocol == nullptr) {
WriteServerErrorResponse(response, "Service protocol unavailable.");
return false;
}
return service_protocol->HandleMessage(method, params, response);
}
[[nodiscard]] static bool HandleMessageOnHandler(
ServiceProtocol::Handler* handler,
std::string_view method,
const ServiceProtocol::Handler::ServiceProtocolMap& params,
rapidjson::Document& document) {
FML_DCHECK(handler);
fml::AutoResetWaitableEvent latch;
bool result = false;
fml::TaskRunner::RunNowOrPostTask(
handler->GetServiceProtocolHandlerTaskRunner(method),
[&latch, //
&result, //
&handler, //
&method, //
&params, //
&document //
]() {
result =
handler->HandleServiceProtocolMessage(method, params, document);
latch.Signal();
});
latch.Wait();
return result;
}
bool ServiceProtocol::HandleMessage(std::string_view method,
const Handler::ServiceProtocolMap& params,
rapidjson::Document& response) const {
if (method == kListViewsExtensionName) {
// So far, this is the only built-in method that does not forward to the
// dynamic set of handlers.
return HandleListViewsMethod(response);
}
fml::SharedLock lock(*handlers_mutex_);
if (handlers_.size() == 0) {
WriteServerErrorResponse(response,
"There are no running service protocol handlers.");
return false;
}
// Find the handler by its "viewId" in the params.
auto view_id_param_found = params.find(std::string_view{"viewId"});
if (view_id_param_found != params.end()) {
auto* handler = reinterpret_cast<Handler*>(std::stoull(
view_id_param_found->second.data() + kViewIdPrefx.size(), nullptr, 16));
auto handler_found = handlers_.find(handler);
if (handler_found != handlers_.end()) {
return HandleMessageOnHandler(handler, method, params, response);
}
}
// Handle legacy calls that do not specify a handler in their args.
// TODO(chinmaygarde): Deprecate these calls in the tools and remove these
// fallbacks.
if (method == kScreenshotExtensionName ||
method == kScreenshotSkpExtensionName ||
method == kFlushUIThreadTasksExtensionName) {
return HandleMessageOnHandler(handlers_.begin()->first, method, params,
response);
}
WriteServerErrorResponse(
response,
"Service protocol could not handle or find a handler for the "
"requested method.");
return false;
}
static std::string CreateFlutterViewID(intptr_t handler) {
std::stringstream stream;
stream << kViewIdPrefx << "0x" << std::hex << handler;
return stream.str();
}
static std::string CreateIsolateID(int64_t isolate) {
std::stringstream stream;
stream << "isolates/" << isolate;
return stream.str();
}
void ServiceProtocol::Handler::Description::Write(
Handler* handler,
rapidjson::Value& view,
rapidjson::MemoryPoolAllocator<>& allocator) const {
view.SetObject();
view.AddMember("type", "FlutterView", allocator);
view.AddMember("id", CreateFlutterViewID(reinterpret_cast<intptr_t>(handler)),
allocator);
if (isolate_port != 0) {
rapidjson::Value isolate(rapidjson::Type::kObjectType);
{
isolate.AddMember("type", "@Isolate", allocator);
isolate.AddMember("fixedId", true, allocator);
isolate.AddMember("id", CreateIsolateID(isolate_port), allocator);
isolate.AddMember("name", isolate_name, allocator);
isolate.AddMember("number", isolate_port, allocator);
}
view.AddMember("isolate", isolate, allocator);
}
}
bool ServiceProtocol::HandleListViewsMethod(
rapidjson::Document& response) const {
fml::SharedLock lock(*handlers_mutex_);
std::vector<std::pair<intptr_t, Handler::Description>> descriptions;
for (const auto& handler : handlers_) {
descriptions.emplace_back(reinterpret_cast<intptr_t>(handler.first),
handler.second.Load());
}
auto& allocator = response.GetAllocator();
// Construct the response objects.
response.SetObject();
response.AddMember("type", "FlutterViewList", allocator);
rapidjson::Value viewsList(rapidjson::Type::kArrayType);
for (const auto& description : descriptions) {
rapidjson::Value view(rapidjson::Type::kObjectType);
description.second.Write(reinterpret_cast<Handler*>(description.first),
view, allocator);
viewsList.PushBack(view, allocator);
}
response.AddMember("views", viewsList, allocator);
return true;
}
} // namespace flutter