2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "extension/extension_server.h"
21 #include <glib-unix.h>
26 #include "common/logger.h"
27 #include "common/constants.h"
28 #include "common/file_utils.h"
29 #include "common/string_utils.h"
30 #include "common/command_line.h"
31 #include "extension/extension.h"
37 const char kExtensionPrefix[] = "lib";
38 const char kExtensionSuffix[] = ".so";
40 const char kDBusIntrospectionXML[] =
42 " <interface name='org.tizen.wrt.Extension'>"
43 " <method name='GetExtensions'>"
44 " <arg name='extensions' type='a(ssas)' direction='out' />"
46 " <method name='CreateInstance'>"
47 " <arg name='extension_name' type='s' direction='in' />"
48 " <arg name='instance_id' type='s' direction='out' />"
50 " <method name='DestroyInstance'>"
51 " <arg name='instance_id' type='s' direction='in' />"
52 " <arg name='instance_id' type='s' direction='out' />"
54 " <method name='PostMessage'>"
55 " <arg name='instance_id' type='s' direction='in' />"
56 " <arg name='msg' type='s' direction='in' />"
58 " <method name='SendSyncMessage'>"
59 " <arg name='instance_id' type='s' direction='in' />"
60 " <arg name='msg' type='s' direction='in' />"
61 " <arg name='reply' type='s' direction='out' />"
63 " <signal name='OnMessageToJS'>"
64 " <arg name='instance_id' type='s' />"
65 " <arg name='msg' type='s' />"
72 ExtensionServer::ExtensionServer(const std::string& uuid)
76 ExtensionServer::~ExtensionServer() {
79 bool ExtensionServer::Start() {
80 return Start(StringVector());
83 bool ExtensionServer::Start(const StringVector& paths) {
84 // Connect to DBusServer for Application of Runtime
85 if (!dbus_application_client_.ConnectByName(
86 app_uuid_ + "." + std::string(kDBusNameForApplication))) {
87 LOGGER(ERROR) << "Failed to connect to the dbus server for Application.";
91 // Register system extensions to support Tizen Device APIs
92 RegisterSystemExtensions();
94 // Register user extensions
95 for (auto it = paths.begin(); it != paths.end(); ++it) {
96 if (utils::Exists(*it)) {
97 RegisterExtension(*it);
102 using std::placeholders::_1;
103 using std::placeholders::_2;
104 using std::placeholders::_3;
105 using std::placeholders::_4;
106 dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
107 dbus_server_.SetMethodCallback(
108 kDBusInterfaceNameForExtension,
109 std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3, _4));
110 dbus_server_.Start(app_uuid_ + "." + std::string(kDBusNameForExtension));
112 // Send 'ready' signal to Injected Bundle.
113 NotifyEPCreatedToApplication();
118 void ExtensionServer::RegisterExtension(const std::string& path) {
119 Extension* ext = new Extension(path, this);
120 if (!ext->Initialize() || !RegisterSymbols(ext)) {
124 extensions_[ext->name()] = ext;
125 LOGGER(DEBUG) << ext->name() << " is registered.";
128 void ExtensionServer::RegisterSystemExtensions() {
129 #ifdef EXTENSION_PATH
130 std::string extension_path(EXTENSION_PATH);
132 #error EXTENSION_PATH is not set.
134 extension_path.append("/");
135 extension_path.append(kExtensionPrefix);
136 extension_path.append("*");
137 extension_path.append(kExtensionSuffix);
140 glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
141 for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
142 RegisterExtension(glob_result.gl_pathv[i]);
146 bool ExtensionServer::RegisterSymbols(Extension* extension) {
147 std::string name = extension->name();
149 if (extension_symbols_.find(name) != extension_symbols_.end()) {
150 LOGGER(WARN) << "Ignoring extension with name already registred. '"
155 Extension::StringVector entry_points = extension->entry_points();
156 for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
157 if (extension_symbols_.find(*it) != extension_symbols_.end()) {
158 LOGGER(WARN) << "Ignoring extension with entry_point already registred. '"
164 for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
165 extension_symbols_.insert(*it);
168 extension_symbols_.insert(name);
173 void ExtensionServer::GetRuntimeVariable(const char* key, char* value,
175 GVariant* ret = dbus_application_client_.Call(
176 kDBusInterfaceNameForApplication, kMethodGetRuntimeVariable,
177 g_variant_new("(s)", key), G_VARIANT_TYPE("(s)"));
180 LOGGER(ERROR) << "Failed to get runtime variable from Application. ("
186 g_variant_get(ret, "(&s)", &v);
187 strncpy(value, v, value_len);
189 g_variant_unref(ret);
192 void ExtensionServer::NotifyEPCreatedToApplication() {
193 dbus_application_client_.Call(
194 kDBusInterfaceNameForApplication, kMethodNotifyEPCreated,
195 g_variant_new("(s)", dbus_server_.GetClientAddress().c_str()),
199 void ExtensionServer::HandleDBusMethod(GDBusConnection* connection,
200 const std::string& method_name,
201 GVariant* parameters,
202 GDBusMethodInvocation* invocation) {
203 if (method_name == kMethodGetExtensions) {
204 OnGetExtensions(invocation);
205 } else if (method_name == kMethodCreateInstance) {
206 gchar* extension_name;
207 g_variant_get(parameters, "(&s)", &extension_name);
208 OnCreateInstance(connection, extension_name, invocation);
209 } else if (method_name == kMethodDestroyInstance) {
211 g_variant_get(parameters, "(&s)", &instance_id);
212 OnDestroyInstance(instance_id, invocation);
213 } else if (method_name == kMethodSendSyncMessage) {
216 g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
217 OnSendSyncMessage(instance_id, msg, invocation);
218 } else if (method_name == kMethodPostMessage) {
221 g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
222 OnPostMessage(instance_id, msg);
226 void ExtensionServer::OnGetExtensions(GDBusMethodInvocation* invocation) {
227 GVariantBuilder builder;
229 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
231 // build an array of extensions
232 auto it = extensions_.begin();
233 for ( ; it != extensions_.end(); ++it) {
234 Extension* ext = it->second;
235 // open container for extension
236 g_variant_builder_open(&builder, G_VARIANT_TYPE("(ssas)"));
237 g_variant_builder_add(&builder, "s", ext->name().c_str());
238 g_variant_builder_add(&builder, "s", ext->javascript_api().c_str());
239 // open container for entry_point
240 g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
241 auto it_entry = ext->entry_points().begin();
242 for ( ; it_entry != ext->entry_points().end(); ++it_entry) {
243 g_variant_builder_add(&builder, "s", (*it_entry).c_str());
245 // close container('as') for entry_point
246 g_variant_builder_close(&builder);
247 // close container('(ssas)') for extension
248 g_variant_builder_close(&builder);
251 GVariant* reply = NULL;
252 if (extensions_.size() == 0) {
253 reply = g_variant_new_array(G_VARIANT_TYPE("(ssas)"), NULL, 0);
255 reply = g_variant_builder_end(&builder);
258 g_dbus_method_invocation_return_value(
259 invocation, g_variant_new_tuple(&reply, 1));
262 void ExtensionServer::OnCreateInstance(
263 GDBusConnection* connection, const std::string& extension_name,
264 GDBusMethodInvocation* invocation) {
265 std::string instance_id = utils::GenerateUUID();
267 // find extension with given the extension name
268 auto it = extensions_.find(extension_name);
269 if (it == extensions_.end()) {
270 LOGGER(ERROR) << "Failed to find extension '" << extension_name << "'";
271 g_dbus_method_invocation_return_error(
272 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
273 "Not found extension %s", extension_name.c_str());
278 ExtensionInstance* instance = it->second->CreateInstance();
280 LOGGER(ERROR) << "Failed to create instance of extension '"
281 << extension_name << "'";
282 g_dbus_method_invocation_return_error(
283 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
284 "Failed to create instance of extension %s", extension_name.c_str());
289 using std::placeholders::_1;
290 instance->SetPostMessageCallback(
291 std::bind(&ExtensionServer::PostMessageToJSCallback,
292 this, connection, instance_id, _1));
294 instances_[instance_id] = instance;
295 g_dbus_method_invocation_return_value(
296 invocation, g_variant_new("(s)", instance_id.c_str()));
299 void ExtensionServer::OnDestroyInstance(
300 const std::string& instance_id, GDBusMethodInvocation* invocation) {
301 // find instance with the given instance id
302 auto it = instances_.find(instance_id);
303 if (it == instances_.end()) {
304 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
305 g_dbus_method_invocation_return_error(
306 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
307 "Not found instance %s", instance_id.c_str());
311 // destroy the instance
312 ExtensionInstance* instance = it->second;
315 instances_.erase(it);
317 g_dbus_method_invocation_return_value(
318 invocation, g_variant_new("(s)", instance_id.c_str()));
321 void ExtensionServer::OnSendSyncMessage(
322 const std::string& instance_id, const std::string& msg,
323 GDBusMethodInvocation* invocation) {
324 // find instance with the given instance id
325 auto it = instances_.find(instance_id);
326 if (it == instances_.end()) {
327 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
328 g_dbus_method_invocation_return_error(
329 invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
330 "Not found instance %s", instance_id.c_str());
334 ExtensionInstance* instance = it->second;
336 using std::placeholders::_1;
337 instance->SetSendSyncReplyCallback(
338 std::bind(&ExtensionServer::SyncReplyCallback, this, _1, invocation));
340 instance->HandleSyncMessage(msg);
342 // reponse will be sent by SyncReplyCallback()
346 void ExtensionServer::OnPostMessage(
347 const std::string& instance_id, const std::string& msg) {
348 auto it = instances_.find(instance_id);
349 if (it == instances_.end()) {
350 LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
354 ExtensionInstance* instance = it->second;
355 instance->HandleMessage(msg);
358 void ExtensionServer::SyncReplyCallback(
359 const std::string& reply, GDBusMethodInvocation* invocation) {
360 g_dbus_method_invocation_return_value(
361 invocation, g_variant_new("(s)", reply.c_str()));
364 void ExtensionServer::PostMessageToJSCallback(
365 GDBusConnection* connection, const std::string& instance_id,
366 const std::string& msg) {
367 if (!connection || g_dbus_connection_is_closed(connection)) {
368 LOGGER(ERROR) << "Client connection is closed already.";
372 dbus_server_.SendSignal(connection,
373 kDBusInterfaceNameForExtension,
374 kSignalOnMessageToJS,
375 g_variant_new("(ss)",
381 bool ExtensionServer::StartExtensionProcess() {
384 loop = g_main_loop_new(NULL, FALSE);
386 // Register Quit Signal Handlers
387 auto quit_callback = [](gpointer data) -> gboolean {
388 GMainLoop* loop = reinterpret_cast<GMainLoop*>(data);
389 g_main_loop_quit(loop);
392 g_unix_signal_add(SIGINT, quit_callback, loop);
393 g_unix_signal_add(SIGTERM, quit_callback, loop);
395 CommandLine* cmd = CommandLine::ForCurrentProcess();
397 // TODO(wy80.choi): Receive extension paths for user defined extensions.
399 // Receive AppID from arguments.
400 if (cmd->arguments().size() < 1) {
401 LOGGER(ERROR) << "uuid is required.";
404 std::string uuid = cmd->arguments()[0];
406 // Start ExtensionServer
407 ExtensionServer server(uuid);
408 if (!server.Start()) {
409 LOGGER(ERROR) << "Failed to start extension server.";
413 LOGGER(INFO) << "extension process has been started.";
415 g_main_loop_run(loop);
417 LOGGER(INFO) << "extension process is exiting.";
419 g_main_loop_unref(loop);