1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#define RAPIDJSON_HAS_STDSTRING 1
6
7#include "flutter/runtime/service_protocol.h"
8
9#include <cstring>
10#include <sstream>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include "flutter/fml/posix_wrappers.h"
16#include "flutter/fml/synchronization/waitable_event.h"
17#include "rapidjson/stringbuffer.h"
18#include "rapidjson/writer.h"
19#include "third_party/dart/runtime/include/dart_tools_api.h"
20
21namespace flutter {
22
23const std::string_view ServiceProtocol::kScreenshotExtensionName =
24 "_flutter.screenshot";
25const std::string_view ServiceProtocol::kScreenshotSkpExtensionName =
26 "_flutter.screenshotSkp";
27const std::string_view ServiceProtocol::kRunInViewExtensionName =
28 "_flutter.runInView";
29const std::string_view ServiceProtocol::kFlushUIThreadTasksExtensionName =
30 "_flutter.flushUIThreadTasks";
31const std::string_view ServiceProtocol::kSetAssetBundlePathExtensionName =
32 "_flutter.setAssetBundlePath";
33const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName =
34 "_flutter.getDisplayRefreshRate";
35const std::string_view ServiceProtocol::kGetSkSLsExtensionName =
36 "_flutter.getSkSLs";
37const std::string_view
38 ServiceProtocol::kEstimateRasterCacheMemoryExtensionName =
39 "_flutter.estimateRasterCacheMemory";
40const std::string_view
41 ServiceProtocol::kRenderFrameWithRasterStatsExtensionName =
42 "_flutter.renderFrameWithRasterStats";
43const std::string_view ServiceProtocol::kReloadAssetFonts =
44 "_flutter.reloadAssetFonts";
45
46static constexpr std::string_view kViewIdPrefx = "_flutterView/";
47static constexpr std::string_view kListViewsExtensionName =
48 "_flutter.listViews";
49
50ServiceProtocol::ServiceProtocol()
51 : endpoints_({
52 // Private
53 kListViewsExtensionName,
54
55 // Public
56 kScreenshotExtensionName,
57 kScreenshotSkpExtensionName,
58 kRunInViewExtensionName,
59 kFlushUIThreadTasksExtensionName,
60 kSetAssetBundlePathExtensionName,
61 kGetDisplayRefreshRateExtensionName,
62 kGetSkSLsExtensionName,
63 kEstimateRasterCacheMemoryExtensionName,
64 kRenderFrameWithRasterStatsExtensionName,
65 kReloadAssetFonts,
66 }),
67 handlers_mutex_(fml::SharedMutex::Create()) {}
68
69ServiceProtocol::~ServiceProtocol() {
70 ToggleHooks(set: false);
71}
72
73void ServiceProtocol::AddHandler(Handler* handler,
74 const Handler::Description& description) {
75 fml::UniqueLock lock(*handlers_mutex_);
76 handlers_.emplace(args&: handler, args: description);
77}
78
79void ServiceProtocol::RemoveHandler(Handler* handler) {
80 fml::UniqueLock lock(*handlers_mutex_);
81 handlers_.erase(k: handler);
82}
83
84void ServiceProtocol::SetHandlerDescription(
85 Handler* handler,
86 const Handler::Description& description) {
87 fml::SharedLock lock(*handlers_mutex_);
88 auto it = handlers_.find(k: handler);
89 if (it != handlers_.end()) {
90 it->second.Store(object: description);
91 }
92}
93
94void ServiceProtocol::ToggleHooks(bool set) {
95 for (const auto& endpoint : endpoints_) {
96 Dart_RegisterIsolateServiceRequestCallback(
97 method: endpoint.data(), // method
98 callback: &ServiceProtocol::HandleMessage, // callback
99 user_data: set ? this : nullptr // user data
100 );
101 }
102}
103
104static void WriteServerErrorResponse(rapidjson::Document* document,
105 const char* message) {
106 document->SetObject();
107 document->AddMember(name: "code", value: -32000, allocator&: document->GetAllocator());
108 rapidjson::Value message_value;
109 message_value.SetString(s: message, allocator&: document->GetAllocator());
110 document->AddMember(name: "message", value&: message_value, allocator&: document->GetAllocator());
111}
112
113bool ServiceProtocol::HandleMessage(const char* method,
114 const char** param_keys,
115 const char** param_values,
116 intptr_t num_params,
117 void* user_data,
118 const char** json_object) {
119 Handler::ServiceProtocolMap params;
120 for (intptr_t i = 0; i < num_params; i++) {
121 params[std::string_view{param_keys[i]}] = std::string_view{param_values[i]};
122 }
123
124#ifndef NDEBUG
125 FML_DLOG(INFO) << "Service protcol method: " << method;
126 FML_DLOG(INFO) << "Arguments: " << params.size();
127 for (intptr_t i = 0; i < num_params; i++) {
128 FML_DLOG(INFO) << " " << i + 1 << ": " << param_keys[i] << " = "
129 << param_values[i];
130 }
131#endif // NDEBUG
132
133 rapidjson::Document document;
134 bool result = HandleMessage(method: std::string_view{method}, //
135 params, //
136 service_protocol: static_cast<ServiceProtocol*>(user_data), //
137 response: &document //
138 );
139 rapidjson::StringBuffer buffer;
140 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
141 document.Accept(handler&: writer);
142 *json_object = fml::strdup(str1: buffer.GetString());
143
144#ifndef NDEBUG
145 FML_DLOG(INFO) << "Response: " << *json_object;
146 FML_DLOG(INFO) << "RPC Result: " << result;
147#endif // NDEBUG
148
149 return result;
150}
151
152bool ServiceProtocol::HandleMessage(std::string_view method,
153 const Handler::ServiceProtocolMap& params,
154 ServiceProtocol* service_protocol,
155 rapidjson::Document* response) {
156 if (service_protocol == nullptr) {
157 WriteServerErrorResponse(document: response, message: "Service protocol unavailable.");
158 return false;
159 }
160
161 return service_protocol->HandleMessage(method, params, response);
162}
163
164[[nodiscard]] static bool HandleMessageOnHandler(
165 ServiceProtocol::Handler* handler,
166 std::string_view method,
167 const ServiceProtocol::Handler::ServiceProtocolMap& params,
168 rapidjson::Document* document) {
169 FML_DCHECK(handler);
170 fml::AutoResetWaitableEvent latch;
171 bool result = false;
172 fml::TaskRunner::RunNowOrPostTask(
173 runner: handler->GetServiceProtocolHandlerTaskRunner(method),
174 task: [&latch, //
175 &result, //
176 &handler, //
177 &method, //
178 &params, //
179 &document //
180 ]() {
181 result =
182 handler->HandleServiceProtocolMessage(method, params, response: document);
183 latch.Signal();
184 });
185 latch.Wait();
186 return result;
187}
188
189bool ServiceProtocol::HandleMessage(std::string_view method,
190 const Handler::ServiceProtocolMap& params,
191 rapidjson::Document* response) const {
192 if (method == kListViewsExtensionName) {
193 // So far, this is the only built-in method that does not forward to the
194 // dynamic set of handlers.
195 return HandleListViewsMethod(response);
196 }
197
198 fml::SharedLock lock(*handlers_mutex_);
199
200 if (handlers_.empty()) {
201 WriteServerErrorResponse(document: response,
202 message: "There are no running service protocol handlers.");
203 return false;
204 }
205
206 // Find the handler by its "viewId" in the params.
207 auto view_id_param_found = params.find(k: std::string_view{"viewId"});
208 if (view_id_param_found != params.end()) {
209 auto* handler = reinterpret_cast<Handler*>(std::stoull(
210 str: view_id_param_found->second.data() + kViewIdPrefx.size(), idx: nullptr, base: 16));
211 auto handler_found = handlers_.find(k: handler);
212 if (handler_found != handlers_.end()) {
213 return HandleMessageOnHandler(handler, method, params, document: response);
214 }
215 }
216
217 // Handle legacy calls that do not specify a handler in their args.
218 // TODO(chinmaygarde): Deprecate these calls in the tools and remove these
219 // fallbacks.
220 if (method == kScreenshotExtensionName ||
221 method == kScreenshotSkpExtensionName ||
222 method == kFlushUIThreadTasksExtensionName) {
223 return HandleMessageOnHandler(handler: handlers_.begin()->first, method, params,
224 document: response);
225 }
226
227 WriteServerErrorResponse(
228 document: response,
229 message: "Service protocol could not handle or find a handler for the "
230 "requested method.");
231 return false;
232}
233
234static std::string CreateFlutterViewID(intptr_t handler) {
235 std::stringstream stream;
236 stream << kViewIdPrefx << "0x" << std::hex << handler;
237 return stream.str();
238}
239
240static std::string CreateIsolateID(int64_t isolate) {
241 std::stringstream stream;
242 stream << "isolates/" << isolate;
243 return stream.str();
244}
245
246void ServiceProtocol::Handler::Description::Write(
247 Handler* handler,
248 rapidjson::Value& view,
249 rapidjson::MemoryPoolAllocator<>& allocator) const {
250 view.SetObject();
251 view.AddMember(name: "type", value: "FlutterView", allocator);
252 view.AddMember(name: "id", value: CreateFlutterViewID(handler: reinterpret_cast<intptr_t>(handler)),
253 allocator);
254 if (isolate_port != 0) {
255 rapidjson::Value isolate(rapidjson::Type::kObjectType);
256 {
257 isolate.AddMember(name: "type", value: "@Isolate", allocator);
258 isolate.AddMember(name: "fixedId", value: true, allocator);
259 isolate.AddMember(name: "id", value: CreateIsolateID(isolate: isolate_port), allocator);
260 isolate.AddMember(name: "name", value: isolate_name, allocator);
261 isolate.AddMember(name: "number", value: isolate_port, allocator);
262 }
263 view.AddMember(name: "isolate", value&: isolate, allocator);
264 }
265}
266
267bool ServiceProtocol::HandleListViewsMethod(
268 rapidjson::Document* response) const {
269 fml::SharedLock lock(*handlers_mutex_);
270 std::vector<std::pair<intptr_t, Handler::Description>> descriptions;
271 for (const auto& handler : handlers_) {
272 descriptions.emplace_back(args: reinterpret_cast<intptr_t>(handler.first),
273 args: handler.second.Load());
274 }
275
276 auto& allocator = response->GetAllocator();
277
278 // Construct the response objects.
279 response->SetObject();
280 response->AddMember(name: "type", value: "FlutterViewList", allocator);
281
282 rapidjson::Value viewsList(rapidjson::Type::kArrayType);
283 for (const auto& description : descriptions) {
284 rapidjson::Value view(rapidjson::Type::kObjectType);
285 description.second.Write(handler: reinterpret_cast<Handler*>(description.first),
286 view, allocator);
287 viewsList.PushBack(value&: view, allocator);
288 }
289
290 response->AddMember(name: "views", value&: viewsList, allocator);
291
292 return true;
293}
294
295} // namespace flutter
296

source code of flutter_engine/flutter/runtime/service_protocol.cc