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