Migrate from 2.4 code repo
[platform/core/context/context-service.git] / src / context_mgr_impl.cpp
1 /*
2  * Copyright (c) 2014 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 <glib.h>
18 #include <string>
19
20 #include <types_internal.h>
21 #include <json.h>
22 #include <provider_iface.h>
23 #include "server.h"
24 #include "context_mgr_impl.h"
25 #include "zone_util_impl.h"
26 #include "access_control/privilege.h"
27
28 /* Analyzer Headers */
29 #include <internal/device_context_provider.h>
30 #include <internal/app_statistics_provider.h>
31 #include <internal/social_statistics_provider.h>
32 #include <internal/media_statistics_provider.h>
33 #include <internal/place_context_provider.h>
34
35 ctx::context_manager_impl::context_manager_impl()
36 {
37 }
38
39 ctx::context_manager_impl::~context_manager_impl()
40 {
41         release();
42 }
43
44 bool ctx::context_manager_impl::init()
45 {
46         IF_FAIL_RETURN_TAG(provider_list.size()==0, false, _W, "Re-initialization");
47
48         try {
49                 /* List of all providers */
50                 load_provider(new ctx::device_context_provider());
51                 load_provider(new ctx::app_statistics_provider());
52                 load_provider(new ctx::social_statistics_provider());
53                 load_provider(new ctx::media_statistics_provider());
54                 load_provider(new ctx::place_context_provider());
55
56         } catch (std::bad_alloc& ba) {
57                 _E("Analyzer loading failed (bad alloc)");
58                 return false;
59         } catch (int e) {
60                 _E("Analyzer loading failed (%#x)", e);
61                 return false;
62         }
63
64         return true;
65 }
66
67 void ctx::context_manager_impl::release()
68 {
69         subject_provider_map.clear();
70
71         for (provider_list_t::iterator it = provider_list.begin(); it != provider_list.end(); ++it) {
72                 delete *it;
73         }
74         provider_list.clear();
75
76         for (request_list_t::iterator it = subscribe_request_list.begin(); it != subscribe_request_list.end(); ++it) {
77                 delete *it;
78         }
79         subscribe_request_list.clear();
80
81         for (request_list_t::iterator it = read_request_list.begin(); it != read_request_list.end(); ++it) {
82                 delete *it;
83         }
84         read_request_list.clear();
85 }
86
87 void ctx::context_manager_impl::load_provider(context_provider_iface* provider)
88 {
89         if (!provider) {
90                 _E("Analyzer NULL");
91                 throw static_cast<int>(ERR_INVALID_PARAMETER);
92         }
93
94         provider_list.push_back(provider);
95
96         if (!provider->init()) {
97                 _E("Analyzer initialization failed");
98                 throw ERR_OPERATION_FAILED;
99         }
100 }
101
102 bool ctx::context_manager_impl::register_provider(const char* subject, ctx::context_provider_iface* cp)
103 {
104         IF_FAIL_RETURN_TAG(subject && cp, false, _E, "Invalid parameter");
105
106         if (subject_provider_map.find(subject) != subject_provider_map.end()) {
107                 _E("The provider for the subject '%s' is already registered.", subject);
108                 return false;
109         }
110
111         _I("Registering provider for '%s'", subject);
112         subject_provider_map[subject] = cp;
113
114         return true;
115 }
116
117 void ctx::context_manager_impl::assign_request(ctx::request_info* request)
118 {
119         int req_type = request->get_type();
120         context_provider_iface *provider = NULL;
121
122         if (req_type != REQ_UNSUBSCRIBE) {
123                 subject_provider_map_t::iterator it = subject_provider_map.find(request->get_subject());
124                 if (it == subject_provider_map.end()) {
125                         _E("Unknown subject '%s'", request->get_subject());
126                         request->reply(ERR_NOT_SUPPORTED);
127                         delete request;
128                         return;
129                 }
130                 provider = it->second;
131         }
132
133         std::string app_id;
134         // If the ClientAppId attribute exists but is empty.
135         if (request->get_description().get(NULL, COMMON_ATTR_CLIENT_APP_ID, &app_id)) {
136                 if (app_id.empty() && request->get_app_id()) {
137                         request->get_description().set(NULL, COMMON_ATTR_CLIENT_APP_ID, request->get_app_id());
138                 }
139         }
140
141         switch (req_type) {
142                 case REQ_SUBSCRIBE:
143                         if (!check_permission(request)) {
144                                 request->reply(ERR_PERMISSION_DENIED);
145                                 delete request;
146                                 break;
147                         }
148                         subscribe(request, provider);
149                         break;
150                 case REQ_UNSUBSCRIBE:
151                         unsubscribe(request);
152                         break;
153                 case REQ_READ:
154                 case REQ_READ_SYNC:
155                         if (!check_permission(request)) {
156                                 request->reply(ERR_PERMISSION_DENIED);
157                                 delete request;
158                                 break;
159                         }
160                         read(request, provider);
161                         break;
162                 case REQ_WRITE:
163                         if (!check_permission(request)) {
164                                 request->reply(ERR_PERMISSION_DENIED);
165                                 delete request;
166                                 break;
167                         }
168                         write(request, provider);
169                         break;
170                 case REQ_SUPPORT:
171                         if (provider->is_supported(request->get_subject(), request->get_zone_name())) {
172                                 request->reply(ERR_NONE);
173                         } else {
174                                 request->reply(ERR_NOT_SUPPORTED);
175                         }
176                         delete request;
177                         break;
178                 default:
179                         _E("Invalid type of request");
180                         delete request;
181         }
182 }
183
184 bool ctx::context_manager_impl::is_supported(const char* subject, const char* zone)
185 {
186         subject_provider_map_t::iterator it = subject_provider_map.find(subject);
187         IF_FAIL_RETURN(it != subject_provider_map.end(), false);
188
189         return it->second->is_supported(subject, zone);
190 }
191
192 ctx::context_manager_impl::request_list_t::iterator
193 ctx::context_manager_impl::find_request(request_list_t& r_list, std::string subject, json& option, const char* zone)
194 {
195         return find_request(r_list.begin(), r_list.end(), subject, option, zone);
196 }
197
198 ctx::context_manager_impl::request_list_t::iterator
199 ctx::context_manager_impl::find_request(request_list_t& r_list, std::string client, int req_id)
200 {
201         request_list_t::iterator it;
202         for (it = r_list.begin(); it != r_list.end(); ++it) {
203                 if (client == (*it)->get_client() && req_id == (*it)->get_id()) {
204                         break;
205                 }
206         }
207         return it;
208 }
209
210 ctx::context_manager_impl::request_list_t::iterator
211 ctx::context_manager_impl::find_request(request_list_t::iterator begin, request_list_t::iterator end, std::string subject, json& option, const char* zone)
212 {
213         //TODO: Do we need to consider the case that the inparam option is a subset of the request description?
214         request_list_t::iterator it;
215         for (it = begin; it != end; ++it) {
216                 if (subject == (*it)->get_subject() && option == (*it)->get_description() && STR_EQ(zone, (*it)->get_zone_name())) {
217                         break;
218                 }
219         }
220         return it;
221 }
222
223 bool ctx::context_manager_impl::check_permission(ctx::request_info* request)
224 {
225         const char* app_id = request->get_app_id();
226         _D("Peer AppID: %s", app_id);
227         IF_FAIL_RETURN_TAG(app_id, false, _E, "AppID NULL");
228         IF_FAIL_RETURN_TAG(!STR_EQ(app_id, TRIGGER_CLIENT_NAME), true, _D, "Skipping permission check for Trigger");
229
230         scope_zone_joiner sz(request->get_zone_name());
231
232         bool allowed = ctx::privilege_manager::is_allowed(app_id, request->get_subject());
233         IF_FAIL_RETURN_TAG(allowed, false, _W, "Permission denied");
234
235         return true;
236 }
237
238 void ctx::context_manager_impl::subscribe(ctx::request_info* request, ctx::context_provider_iface* provider)
239 {
240         _I(CYAN("[%s] '%s' subscribes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
241
242         ctx::json request_result;
243         int error = provider->subscribe(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name());
244
245         _D("Analyzer returned %d", error);
246
247         if (!request->reply(error, request_result) || error != ERR_NONE) {
248                 delete request;
249                 return;
250         }
251
252         subscribe_request_list.push_back(request);
253 }
254
255 void ctx::context_manager_impl::unsubscribe(ctx::request_info* request)
256 {
257         _I(CYAN("[%s] '%s' unsubscribes RID-%d"), request->get_zone_name(), request->get_client(), request->get_id());
258
259         // Search the subscribe request to be removed
260         request_list_t::iterator target = find_request(subscribe_request_list, request->get_client(), request->get_id());
261         if (target == subscribe_request_list.end()) {
262                 _W("Unknown request");
263                 delete request;
264                 return;
265         }
266
267         // Keep the pointer to the request found
268         request_info *req_found = *target;
269
270         // Remove the request from the list
271         subscribe_request_list.erase(target);
272
273         // Check if there exist the same requests
274         if (find_request(subscribe_request_list, req_found->get_subject(), req_found->get_description(), req_found->get_zone_name()) != subscribe_request_list.end()) {
275                 // Do not stop detecting the subject
276                 _D("A same request from '%s' exists", req_found->get_client());
277                 request->reply(ERR_NONE);
278                 delete request;
279                 delete req_found;
280                 return;
281         }
282
283         // Find the proper provider
284         subject_provider_map_t::iterator ca = subject_provider_map.find(req_found->get_subject());
285         if (ca == subject_provider_map.end()) {
286                 _E("Invalid subject '%s'", req_found->get_subject());
287                 delete request;
288                 delete req_found;
289                 return;
290         }
291
292         // Stop detecting the subject
293         int error = ca->second->unsubscribe(req_found->get_subject(), req_found->get_description(), req_found->get_zone_name());
294         request->reply(error);
295         delete request;
296         delete req_found;
297 }
298
299 void ctx::context_manager_impl::read(ctx::request_info* request, ctx::context_provider_iface* provider)
300 {
301         _I(CYAN("[%s] '%s' reads '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
302
303         ctx::json request_result;
304         int error = provider->read(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name());
305
306         _D("Analyzer returned %d", error);
307
308         if (!request->reply(error, request_result) || error != ERR_NONE) {
309                 delete request;
310                 return;
311         }
312
313         read_request_list.push_back(request);
314 }
315
316 void ctx::context_manager_impl::write(ctx::request_info* request, ctx::context_provider_iface* provider)
317 {
318         _I(CYAN("[%s] '%s' writes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
319
320         ctx::json request_result;
321         int error = provider->write(request->get_subject(), request->get_description(), &request_result, request->get_zone_name());
322
323         _D("Analyzer returned %d", error);
324
325         request->reply(error, request_result);
326         delete request;
327 }
328
329 bool ctx::context_manager_impl::_publish(const char* subject, ctx::json option, int error, ctx::json data_updated, const char* zone)
330 {
331         IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
332
333         _I("Publishing '%s'", subject);
334         _J("Option", option);
335
336         request_list_t::iterator end = subscribe_request_list.end();
337         request_list_t::iterator target = find_request(subscribe_request_list.begin(), end, subject, option, zone);
338
339         while (target != end) {
340                 if (!(*target)->publish(error, data_updated)) {
341                         return false;
342                 }
343                 target = find_request(++target, end, subject, option, zone);
344         }
345
346         return true;
347 }
348
349 bool ctx::context_manager_impl::_reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read, const char* zone)
350 {
351         IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
352
353         _I("Sending data of '%s'", subject);
354         _J("Option", option);
355         _J("Data", data_read);
356         _SI("Zone: '%s'", zone);
357
358         request_list_t::iterator end = read_request_list.end();
359         request_list_t::iterator target = find_request(read_request_list.begin(), end, subject, option, zone);
360         request_list_t::iterator prev;
361
362         ctx::json dummy;
363
364         while (target != end) {
365                 (*target)->reply(error, dummy, data_read);
366                 prev = target;
367                 target = find_request(++target, end, subject, option, zone);
368
369                 delete *prev;
370                 read_request_list.erase(prev);
371         }
372
373         return true;
374 }
375
376 struct published_data_s {
377         int type;
378         ctx::context_manager_impl *mgr;
379         std::string subject;
380         std::string zone;
381         int error;
382         ctx::json option;
383         ctx::json data;
384         published_data_s(int t, ctx::context_manager_impl *m, const char* s, ctx::json& o, int e, ctx::json& d, const char* z)
385                 : type(t), mgr(m), subject(s), error(e)
386         {
387                 option = o.str();
388                 data = d.str();
389                 zone = z;
390         }
391 };
392
393 gboolean ctx::context_manager_impl::thread_switcher(gpointer data)
394 {
395         published_data_s *tuple = static_cast<published_data_s*>(data);
396
397         switch (tuple->type) {
398                 case REQ_SUBSCRIBE:
399                         tuple->mgr->_publish(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str());
400                         break;
401                 case REQ_READ:
402                         tuple->mgr->_reply_to_read(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str());
403                         break;
404                 default:
405                         _W("Invalid type");
406         }
407
408         delete tuple;
409         return FALSE;
410 }
411
412 bool ctx::context_manager_impl::publish(const char* subject, ctx::json& option, int error, ctx::json& data_updated, const char* zone)
413 {
414         IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter");
415
416         published_data_s *tuple = new(std::nothrow) published_data_s(REQ_SUBSCRIBE, this, subject, option, error, data_updated, zone);
417         IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed");
418
419         g_idle_add(thread_switcher, tuple);
420
421         return true;
422 }
423
424 bool ctx::context_manager_impl::reply_to_read(const char* subject, ctx::json& option, int error, ctx::json& data_read, const char* zone)
425 {
426         IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter");
427
428         published_data_s *tuple = new(std::nothrow) published_data_s(REQ_READ, this, subject, option, error, data_read, zone);
429         IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed");
430
431         g_idle_add(thread_switcher, tuple);
432
433         return true;
434 }