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 | |
22 | namespace flutter { |
23 | |
24 | const std::string_view ServiceProtocol::kScreenshotExtensionName = |
25 | "_flutter.screenshot" ; |
26 | const std::string_view ServiceProtocol::kScreenshotSkpExtensionName = |
27 | "_flutter.screenshotSkp" ; |
28 | const std::string_view ServiceProtocol::kRunInViewExtensionName = |
29 | "_flutter.runInView" ; |
30 | const std::string_view ServiceProtocol::kFlushUIThreadTasksExtensionName = |
31 | "_flutter.flushUIThreadTasks" ; |
32 | const std::string_view ServiceProtocol::kSetAssetBundlePathExtensionName = |
33 | "_flutter.setAssetBundlePath" ; |
34 | const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName = |
35 | "_flutter.getDisplayRefreshRate" ; |
36 | const std::string_view ServiceProtocol::kGetSkSLsExtensionName = |
37 | "_flutter.getSkSLs" ; |
38 | const std::string_view |
39 | ServiceProtocol::kEstimateRasterCacheMemoryExtensionName = |
40 | "_flutter.estimateRasterCacheMemory" ; |
41 | |
42 | static constexpr std::string_view kViewIdPrefx = "_flutterView/" ; |
43 | static constexpr std::string_view kListViewsExtensionName = |
44 | "_flutter.listViews" ; |
45 | |
46 | ServiceProtocol::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 | |
63 | ServiceProtocol::~ServiceProtocol() { |
64 | ToggleHooks(false); |
65 | } |
66 | |
67 | void ServiceProtocol::AddHandler(Handler* handler, |
68 | Handler::Description description) { |
69 | fml::UniqueLock lock(*handlers_mutex_); |
70 | handlers_.emplace(handler, description); |
71 | } |
72 | |
73 | void ServiceProtocol::RemoveHandler(Handler* handler) { |
74 | fml::UniqueLock lock(*handlers_mutex_); |
75 | handlers_.erase(handler); |
76 | } |
77 | |
78 | void 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 | |
87 | void 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 | |
97 | static 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 | |
106 | bool 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 | |
145 | bool 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 | ¶ms, // |
172 | &document // |
173 | ]() { |
174 | result = |
175 | handler->HandleServiceProtocolMessage(method, params, document); |
176 | latch.Signal(); |
177 | }); |
178 | latch.Wait(); |
179 | return result; |
180 | } |
181 | |
182 | bool 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 | |
227 | static std::string CreateFlutterViewID(intptr_t handler) { |
228 | std::stringstream stream; |
229 | stream << kViewIdPrefx << "0x" << std::hex << handler; |
230 | return stream.str(); |
231 | } |
232 | |
233 | static std::string CreateIsolateID(int64_t isolate) { |
234 | std::stringstream stream; |
235 | stream << "isolates/" << isolate; |
236 | return stream.str(); |
237 | } |
238 | |
239 | void 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 | |
260 | bool 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 | |