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