1 // Copyright 2015 Samsung Electronics Co, Ltd. 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.
5 #include "extension/extension_server.h"
14 #include "common/logger.h"
15 #include "common/constants.h"
16 #include "common/file_utils.h"
17 #include "common/string_utils.h"
18 #include "common/command_line.h"
19 #include "extension/extension.h"
25 const char kExtensionPrefix[] = "lib";
26 const char kExtensionSuffix[] = ".so";
28 const char kDBusIntrospectionXML[] =
30 " <interface name='org.tizen.wrt.Extension'>"
31 " <method name='GetExtensions'>"
32 " <arg name='extensions' type='a(ssas)' direction='out' />"
34 " <method name='CreateInstance'>"
35 " <arg name='extension_name' type='s' direction='in' />"
36 " <arg name='instance_id' type='s' direction='out' />"
38 " <method name='DestroyInstance'>"
39 " <arg name='instance_id' type='s' direction='in' />"
40 " <arg name='instance_id' type='s' direction='out' />"
42 " <method name='PostMessage'>"
43 " <arg name='instance_id' type='s' direction='in' />"
44 " <arg name='msg' type='s' direction='in' />"
46 " <method name='SendSyncMessage'>"
47 " <arg name='instance_id' type='s' direction='in' />"
48 " <arg name='msg' type='s' direction='in' />"
49 " <arg name='reply' type='s' direction='out' />"
51 " <signal name='OnMessageToJS'>"
52 " <arg name='instance_id' type='s' />"
53 " <arg name='msg' type='s' />"
60 ExtensionServer::ExtensionServer(const std::string& uuid)
64 ExtensionServer::~ExtensionServer() {
67 bool ExtensionServer::Start() {
68 return Start(StringVector());
71 bool ExtensionServer::Start(const StringVector& paths) {
72 // Connect to DBusServer for Application of Runtime
73 if (!dbus_application_client_.ConnectByName(
74 app_uuid_ + "." + std::string(kDBusNameForApplication))) {
75 LOGGER(ERROR) << "Failed to connect to the dbus server for Application.";
79 // Register system extensions to support Tizen Device APIs
80 RegisterSystemExtensions();
82 // Register user extensions
83 for (auto it = paths.begin(); it != paths.end(); ++it) {
84 if (utils::Exists(*it)) {
85 RegisterExtension(*it);
90 using std::placeholders::_1;
91 using std::placeholders::_2;
92 using std::placeholders::_3;
93 using std::placeholders::_4;
94 dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
95 dbus_server_.SetMethodCallback(
96 kDBusInterfaceNameForExtension,
97 std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3, _4));
98 dbus_server_.Start(app_uuid_ + "." + std::string(kDBusNameForExtension));
100 // Send 'ready' signal to Injected Bundle.
101 NotifyEPCreatedToApplication();
106 void ExtensionServer::RegisterExtension(const std::string& path) {
107 Extension* ext = new Extension(path, this);
108 if (!ext->Initialize() || !RegisterSymbols(ext)) {
112 extensions_[ext->name()] = ext;
113 LOGGER(DEBUG) << ext->name() << " is registered.";
116 void ExtensionServer::RegisterSystemExtensions() {
117 #ifdef EXTENSION_PATH
118 std::string extension_path(EXTENSION_PATH);
120 #error EXTENSION_PATH is not set.
122 extension_path.append("/");
123 extension_path.append(kExtensionPrefix);
124 extension_path.append("*");
125 extension_path.append(kExtensionSuffix);
128 glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
129 for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
130 RegisterExtension(glob_result.gl_pathv[i]);
134 bool ExtensionServer::RegisterSymbols(Extension* extension) {
135 std::string name = extension->name();
137 if (extension_symbols_.find(name) != extension_symbols_.end()) {
138 LOGGER(WARN) << "Ignoring extension with name already registred. '"
143 Extension::StringVector entry_points = extension->entry_points();
144 for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
145 if (extension_symbols_.find(*it) != extension_symbols_.end()) {
146 LOGGER(WARN) << "Ignoring extension with entry_point already registred. '"
152 for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
153 extension_symbols_.insert(*it);
156 extension_symbols_.insert(name);
161 void ExtensionServer::GetRuntimeVariable(const char* key, char* value,
163 GVariant* ret = dbus_application_client_.Call(
164 kDBusInterfaceNameForApplication, kMethodGetRuntimeVariable,
165 g_variant_new("(s)", key), G_VARIANT_TYPE("(s)"));
168 LOGGER(ERROR) << "Failed to get runtime variable from Application. ("
174 g_variant_get(ret, "(&s)", &v);
175 strncpy(value, v, value_len);
177 g_variant_unref(ret);
180 void ExtensionServer::NotifyEPCreatedToApplication() {
181 dbus_application_client_.Call(
182 kDBusInterfaceNameForApplication, kMethodNotifyEPCreated,
183 g_variant_new("(s)", dbus_server_.GetClientAddress().c_str()),
187 void ExtensionServer::HandleDBusMethod(GDBusConnection* connection,
188 const std::string& method_name,
189 GVariant* parameters,
190 GDBusMethodInvocation* invocation) {
191 if (method_name == kMethodGetExtensions) {
192 OnGetExtensions(invocation);
193 } else if (method_name == kMethodCreateInstance) {
194 gchar* extension_name;
195 g_variant_get(parameters, "(&s)", &extension_name);
196 OnCreateInstance(connection, extension_name, invocation);
197 } else if (method_name == kMethodDestroyInstance) {
199 g_variant_get(parameters, "(&s)", &instance_id);
200 OnDestroyInstance(instance_id, invocation);
201 } else if (method_name == kMethodSendSyncMessage) {
204 g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
205 OnSendSyncMessage(instance_id, msg, invocation);
206 } else if (method_name == kMethodPostMessage) {
209 g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
210 OnPostMessage(instance_id, msg);
214 void ExtensionServer::OnGetExtensions(GDBusMethodInvocation* invocation) {
215 GVariantBuilder builder;
217 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
219 // build an array of extensions
220 auto it = extensions_.begin();
221 for ( ; it != extensions_.end(); ++it) {
222 Extension* ext = it->second;
223 // open container for extension
224 g_variant_builder_open(&builder, G_VARIANT_TYPE("(ssas)"));
225 g_variant_builder_add(&builder, "s", ext->name().c_str());
226 g_variant_builder_add(&builder, "s", ext->javascript_api().c_str());
227 // open container for entry_point
228 g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
229 auto it_entry = ext->entry_points().begin();
230 for ( ; it_entry != ext->entry_points().end(); ++it_entry) {
231 g_variant_builder_add(&builder, "s", (*it_entry).c_str());
233 // close container('as') for entry_point
234 g_variant_builder_close(&builder);
235 // close container('(ssas)') for extension
236 g_variant_builder_close(&builder);
239 GVariant* reply = NULL;
240 if (extensions_.size() == 0) {
241 reply = g_variant_new_array(G_VARIANT_TYPE("(ssas)"), NULL, 0);
243 reply = g_variant_builder_end(&builder);
246 g_dbus_method_invocation_return_value(
247 invocation, g_variant_new_tuple(&reply, 1));
250 void ExtensionServer::OnCreateInstance(
251 GDBusConnection* connection, const std::string& extension_name,
252 GDBusMethodInvocation* invocation) {
253 std::string instance_id = utils::GenerateUUID();
255 // find extension with given the extension name
256 auto it = extensions_.find(extension_name);
257 if (it == extensions_.end()) {
258 LOGGER(ERROR) << "Failed to find extension '" << extension_name << "'";
259 g_dbus_method_invocation_return_error(
260 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
261 "Not found extension %s", extension_name.c_str());
266 ExtensionInstance* instance = it->second->CreateInstance();
268 LOGGER(ERROR) << "Failed to create instance of extension '"
269 << extension_name << "'";
270 g_dbus_method_invocation_return_error(
271 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
272 "Failed to create instance of extension %s", extension_name.c_str());
277 using std::placeholders::_1;
278 instance->SetPostMessageCallback(
279 std::bind(&ExtensionServer::PostMessageToJSCallback,
280 this, connection, instance_id, _1));
282 instances_[instance_id] = instance;
283 g_dbus_method_invocation_return_value(
284 invocation, g_variant_new("(s)", instance_id.c_str()));
287 void ExtensionServer::OnDestroyInstance(
288 const std::string& instance_id, GDBusMethodInvocation* invocation) {
289 // find instance with the given instance id
290 auto it = instances_.find(instance_id);
291 if (it == instances_.end()) {
292 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
293 g_dbus_method_invocation_return_error(
294 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
295 "Not found instance %s", instance_id.c_str());
299 // destroy the instance
300 ExtensionInstance* instance = it->second;
303 instances_.erase(it);
305 g_dbus_method_invocation_return_value(
306 invocation, g_variant_new("(s)", instance_id.c_str()));
309 void ExtensionServer::OnSendSyncMessage(
310 const std::string& instance_id, const std::string& msg,
311 GDBusMethodInvocation* invocation) {
312 // find instance with the given instance id
313 auto it = instances_.find(instance_id);
314 if (it == instances_.end()) {
315 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
316 g_dbus_method_invocation_return_error(
317 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
318 "Not found instance %s", instance_id.c_str());
322 ExtensionInstance* instance = it->second;
324 using std::placeholders::_1;
325 instance->SetSendSyncReplyCallback(
326 std::bind(&ExtensionServer::SyncReplyCallback, this, _1, invocation));
328 instance->HandleSyncMessage(msg);
330 // reponse will be sent by SyncReplyCallback()
334 void ExtensionServer::OnPostMessage(
335 const std::string& instance_id, const std::string& msg) {
336 auto it = instances_.find(instance_id);
337 if (it == instances_.end()) {
338 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
342 ExtensionInstance* instance = it->second;
343 instance->HandleMessage(msg);
346 void ExtensionServer::SyncReplyCallback(
347 const std::string& reply, GDBusMethodInvocation* invocation) {
348 g_dbus_method_invocation_return_value(
349 invocation, g_variant_new("(s)", reply.c_str()));
352 void ExtensionServer::PostMessageToJSCallback(
353 GDBusConnection* connection, const std::string& instance_id,
354 const std::string& msg) {
355 if (!connection || g_dbus_connection_is_closed(connection)) {
356 LOGGER(ERROR) << "Client connection is closed already.";
360 dbus_server_.SendSignal(connection,
361 kDBusInterfaceNameForExtension,
362 kSignalOnMessageToJS,
363 g_variant_new("(ss)",
369 bool ExtensionServer::StartExtensionProcess() {
372 loop = g_main_loop_new(NULL, FALSE);
374 // Register Quit Signal Handlers
375 auto quit_callback = [](gpointer data) -> gboolean {
376 GMainLoop* loop = reinterpret_cast<GMainLoop*>(data);
377 g_main_loop_quit(loop);
380 g_unix_signal_add(SIGINT, quit_callback, loop);
381 g_unix_signal_add(SIGTERM, quit_callback, loop);
383 CommandLine* cmd = CommandLine::ForCurrentProcess();
385 // TODO(wy80.choi): Receive extension paths for user defined extensions.
387 // Receive AppID from arguments.
388 if (cmd->arguments().size() < 1) {
389 LOGGER(ERROR) << "uuid is required.";
392 std::string uuid = cmd->arguments()[0];
394 // Start ExtensionServer
395 ExtensionServer server(uuid);
396 if (!server.Start()) {
397 LOGGER(ERROR) << "Failed to start extension server.";
401 LOGGER(INFO) << "extension process has been started.";
403 g_main_loop_run(loop);
405 LOGGER(INFO) << "extension process is exiting.";
407 g_main_loop_unref(loop);