a20539ec187baaa17f830c05ef3696b2d09cc311
[platform/core/context/context-common.git] / src / server / ServiceBase.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <atomic>
18 #include <ScopeMutex.h>
19 #include <ClientBase.h>
20 #include <MethodCall.h>
21 #include <ServiceBase.h>
22
23 using namespace ctx;
24
25 static std::atomic_uint __activeUid;
26
27 bool ServiceBase::__singleThreading = false;
28
29 ServiceBase::ServiceBase(GDBusConnection* conn, const char* serviceName, const char* methodSpecs) :
30         __running(false),
31         __serviceName(serviceName),
32         __mainContext(NULL),
33         __mainLoop(NULL),
34         __gthread(NULL),
35         __connection(conn),
36         __methodSpecs(methodSpecs),
37         __objPath(CTX_DBUS_PATH),
38         __interface(CTX_DBUS_IFACE),
39         __nodeInfo(NULL),
40         __registrationId(0)
41 {
42         __objPath += serviceName;
43         __interface += serviceName;
44 }
45
46 ServiceBase::~ServiceBase()
47 {
48 }
49
50 GDBusConnection* ServiceBase::getConnection()
51 {
52         return __connection;
53 }
54
55 bool ServiceBase::start()
56 {
57         IF_FAIL_RETURN(!__running, true);
58         __running = true;
59
60         _I("Preparing '%s'", __serviceName);
61
62         if (__singleThreading) {
63                 _I(CYAN("'%s' runs on the global main loop"), __serviceName);
64                 IF_FAIL_RETURN(__init(), false);
65         } else {
66                 __gthread = g_thread_new(__serviceName, __threadFunc, this);
67                 IF_FAIL_RETURN_TAG(__gthread, false, _E, "Thread creation failed");
68         }
69
70         return true;
71 }
72
73 void ServiceBase::stop()
74 {
75         IF_FAIL_VOID(__running);
76         __running = false;
77
78         if (__singleThreading) {
79                 __release();
80         } else {
81                 if (__mainLoop && g_main_loop_is_running(__mainLoop)) {
82                         GSource *gSrc = g_idle_source_new();
83                         if (gSrc) {
84                                 // Tries to stop the main loop within its thread.
85                                 // In this way, already scheduled idle tasks are not discarded.
86                                 g_source_set_callback(gSrc, __stopMainLoop, this, NULL);
87                                 g_source_attach(gSrc, __mainContext);
88                         } else {
89                                 __stopMainLoop(this);
90                         }
91                 }
92
93                 if (__gthread) {
94                         _I("Joining the thread of '%s'", __serviceName);
95                         g_thread_join(__gthread);
96                 }
97         }
98 }
99
100 gboolean ServiceBase::__stopMainLoop(gpointer data)
101 {
102         ServiceBase* svc = static_cast<ServiceBase*>(data);
103         _I(PURPLE("Stopping '%s'"), svc->__serviceName);
104         g_main_loop_quit(svc->__mainLoop);
105         return G_SOURCE_REMOVE;
106 }
107
108 void ServiceBase::publish(const std::string& busName, const std::string& signalName, GVariant* param)
109 {
110         GError* gerr = NULL;
111         g_dbus_connection_emit_signal(__connection,
112                         busName.c_str(), __objPath.c_str(), __interface.c_str(),
113                         signalName.c_str(), param, &gerr);
114         HANDLE_GERROR(gerr);
115 }
116
117 uid_t ServiceBase::getActiveUser()
118 {
119         return __activeUid.load();
120 }
121
122 void ServiceBase::setActiveUser(uid_t uid)
123 {
124         __activeUid.store(uid);
125 }
126
127 void ServiceBase::onUserActivated()
128 {
129 }
130
131 void ServiceBase::onUserDeactivated()
132 {
133 }
134
135 void ServiceBase::notifyUserNew()
136 {
137         GSource* gSrc = g_idle_source_new();
138         IF_FAIL_VOID_TAG(gSrc, _E, "Memory allocation failed");
139
140         g_source_set_callback(gSrc, __onUserActivated, this, NULL);
141         g_source_attach(gSrc, __mainContext);
142 }
143
144 void ServiceBase::notifyUserRemoved()
145 {
146         GSource* gSrc = g_idle_source_new();
147         IF_FAIL_VOID_TAG(gSrc, _E, "Memory allocation failed");
148
149         g_source_set_callback(gSrc, __onUserDeactivated, this, NULL);
150         g_source_attach(gSrc, __mainContext);
151 }
152
153 gboolean ServiceBase::__onUserActivated(gpointer data)
154 {
155         ServiceBase* svc = static_cast<ServiceBase*>(data);
156         svc->onUserActivated();
157         return G_SOURCE_REMOVE;
158 }
159
160 gboolean ServiceBase::__onUserDeactivated(gpointer data)
161 {
162         ServiceBase* svc = static_cast<ServiceBase*>(data);
163         svc->onUserDeactivated();
164         return G_SOURCE_REMOVE;
165 }
166
167 gpointer ServiceBase::__threadFunc(gpointer data)
168 {
169         ServiceBase* service = static_cast<ServiceBase*>(data);
170         service->__run();
171         return NULL;
172 }
173
174 void ServiceBase::__run()
175 {
176         if (!__init()) {
177                 _E("Starting failed");
178                 __release();
179                 return;
180         }
181
182         _I(CYAN("Starting '%s'"), __serviceName);
183         g_main_loop_run(__mainLoop);
184
185         __release();
186 }
187
188 bool ServiceBase::__init()
189 {
190         GError* gerr = NULL;
191         GDBusInterfaceVTable vtable;
192
193         vtable.method_call = __onMethodCalled;
194         vtable.get_property = NULL;
195         vtable.set_property = NULL;
196
197         if (!__singleThreading) {
198                 __mainContext = g_main_context_new();
199                 IF_FAIL_RETURN_TAG(__mainContext, false, _E, "MainContext creation failed");
200
201                 g_main_context_push_thread_default(__mainContext);
202
203                 __mainLoop = g_main_loop_new(__mainContext, FALSE);
204                 IF_FAIL_RETURN_TAG(__mainLoop, false, _E, "MainLoop creation failed");
205         }
206
207         std::string introspection("<node><interface name='");
208         introspection += __interface + "'>" + __methodSpecs + "</interface></node>";
209
210         __nodeInfo = g_dbus_node_info_new_for_xml(introspection.c_str(), NULL);
211         IF_FAIL_RETURN_TAG(__nodeInfo, false, _E, "NodeInfo creation failed");
212
213         __registrationId = g_dbus_connection_register_object(__connection,
214                         __objPath.c_str(), __nodeInfo->interfaces[0], &vtable, this, NULL, &gerr);
215         HANDLE_GERROR(gerr);
216         IF_FAIL_RETURN_TAG(__registrationId > 0, false, _E, "Object registration failed");
217
218         return prepare();
219 }
220
221 void ServiceBase::__release()
222 {
223         _D("Releasing '%s'", __serviceName);
224
225         for (auto iter = __clients.begin(); iter != __clients.end(); ++iter) {
226                 iter->second.client->onDisconnected();
227                 delete iter->second.client;
228         }
229
230         __clients.clear();
231
232         cleanup();
233
234         if (__registrationId > 0)
235                 g_dbus_connection_unregister_object(__connection, __registrationId);
236
237         if (__nodeInfo)
238                 g_dbus_node_info_unref(__nodeInfo);
239
240         if (__mainLoop)
241                 g_main_loop_unref(__mainLoop);
242
243         if (__mainContext)
244                 g_main_context_unref(__mainContext);
245 }
246
247 void ServiceBase::__onMethodCalled(GDBusConnection* conn, const gchar* sender,
248                 const gchar* path, const gchar* iface, const gchar* name,
249                 GVariant* param, GDBusMethodInvocation* invocation, gpointer userData)
250 {
251         ServiceBase* service = static_cast<ServiceBase*>(userData);
252         service->__onMethodCalled(sender, name, param, invocation);
253 }
254
255 void ServiceBase::__onMethodCalled(const std::string& sender,
256                 const std::string& name, GVariant* param, GDBusMethodInvocation* invocation)
257 {
258         _I("'%s' called '%s.%s'", sender.c_str(), __serviceName, name.c_str());
259
260         ClientBase* client = __getClient(sender);
261         IF_FAIL_VOID(client);
262
263         MethodCall* methodCall = new(std::nothrow) MethodCall(client, name, param, invocation);
264         IF_FAIL_VOID_TAG(methodCall, _E, "Memory allocation failed");
265
266         client->onMethodCalled(methodCall);
267 }
268
269 ClientBase* ServiceBase::__getClient(const std::string& busName)
270 {
271         auto iter = __clients.find(busName);
272
273         if (iter != __clients.end())
274                 return iter->second.client;
275
276         ClientBase* client = createClient(busName);
277         IF_FAIL_RETURN_TAG(client, NULL, _E, "Memory allocation failed");
278
279         if (!client->isVerified()) {
280                 delete client;
281                 return NULL;
282         }
283
284         ClientInfo info = {client, __watch(busName, client)};
285         __clients[busName] = info;
286
287         return client;
288 }
289
290 void ServiceBase::__onNameOwnerChanged(GDBusConnection* conn, const gchar* sender,
291                 const gchar* path, const gchar* iface, const gchar* name,
292                 GVariant* param, gpointer userData)
293 {
294         ClientBase* client = static_cast<ClientBase*>(userData);
295         client->getHostService().__removeClient(client->getBusName());
296 }
297
298 void ServiceBase::__removeClient(const std::string& busName)
299 {
300         _I("'%s' lost '%s'", __serviceName, busName.c_str());
301
302         auto iter = __clients.find(busName);
303         IF_FAIL_VOID(iter != __clients.end());
304
305         ClientInfo info = iter->second;
306         __clients.erase(iter);
307
308         __unwatch(info.watchId);
309         info.client->onDisconnected();
310         delete info.client;
311 }
312
313 unsigned int ServiceBase::__watch(const std::string& busName, ClientBase* client)
314 {
315         return g_dbus_connection_signal_subscribe(__connection,
316                         "org.freedesktop.DBus", "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus",
317                         busName.c_str(), G_DBUS_SIGNAL_FLAGS_NONE, __onNameOwnerChanged, client, NULL);
318 }
319
320 void ServiceBase::__unwatch(unsigned int watchId)
321 {
322         g_dbus_connection_signal_unsubscribe(__connection, watchId);
323 }
324
325 void ServiceBase::setSingleThreading()
326 {
327         __singleThreading = true;
328 }