trigger: implement API functions using the internal job scheduler API
[platform/core/api/context.git] / src / trigger / context_trigger.cpp
1 /*
2  * Copyright (c) 2015 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 <string>
18 #include <vector>
19 #include <set>
20 #include <app_control_internal.h>
21 #include <aul.h>
22 #include <pkgmgr-info.h>
23
24 #include <ContextTypes.h>
25 #include <SharedUtil.h>
26 #include <ScopeMutex.h>
27 #include <job_scheduler_internal.h>
28 #include <context_trigger.h>
29 #include <context_trigger_internal.h>
30
31 #include "PrivilegeChecker.h"
32 #include "ContextItem.h"
33 #include "CustomTemplate.h"
34
35 #define _DEPRECATED_FUNC                _W("DEPRECATION WARNING: %s is deprecated and will be removed from next release.", __FUNCTION__)
36 #define _DEPRECATED_EVENT(id)   _W("DEPRECATION WARNING: This event (%d) is deprecated and will be removed from next release.", (id))
37 #define _DEPRECATED_COND(id)    _W("DEPRECATION WARNING: This condition (%d) is deprecated and will be removed from next release.", (id))
38
39 #define ASSERT_ALLOC(X)         IF_FAIL_RETURN_TAG(X, E_NO_MEM, _E, E_STR_ALLOC)
40 #define ASSERT_NOT_NULL(X)      IF_FAIL_RETURN_TAG(X, E_PARAM, _E, "Parameter null")
41 #define INIT_SCHED                      IF_FAIL_RETURN_TAG(__scheduler.get(), E_SUPPORT, _E, "Job scheduler is not supported")
42 #define SCHED                           __scheduler.get()
43
44 #define PKG_ID_LENGTH 128
45
46 enum EntryCategory {
47         CATEGORY_EVENT          = 1,
48         CATEGORY_CONDITION      = 2
49 };
50
51 enum class OpType {
52         UNDEFINED = 0,
53         EQ,     // equals to
54         NE,     // not equals to
55         GT,     // greater than
56         GE,     // greater than or equals to
57         LT,     // less than
58         LE      // less than or equals to
59 };
60
61 typedef struct _context_trigger_rule_s {
62         ctx_sched_job_h job;
63         bool readOnly;
64         bool hasEvent;
65         bool hasAction;
66         std::set<std::string> conditions;
67
68         _context_trigger_rule_s() :
69                 job(NULL), readOnly(false), hasEvent(false), hasAction(false)
70         {
71                 ctx_sched_job_create_on_demand(&job);
72         }
73
74         _context_trigger_rule_s(ctx_sched_job_h j) :
75                 job(j), readOnly(true), hasEvent(true), hasAction(true)
76         {
77         }
78
79         ~_context_trigger_rule_s()
80         {
81                 ctx_sched_job_destroy(job);
82         }
83 } _context_trigger_rule_h;
84
85
86 typedef struct _context_trigger_rule_entry_s {
87         int category;
88         int type;
89         std::string uri;
90         ctx_sched_job_context_h jobContext;
91         std::set<std::string> conjunctionKeys;
92         std::set<std::string> disjunctionKeys;
93
94         _context_trigger_rule_entry_s(int c, int t, const char* u) :
95                 category(c), type(t), uri(u), jobContext(NULL)
96         {
97                 if (category == CATEGORY_EVENT)
98                         ctx_sched_job_trigger_create(uri.c_str(), &jobContext);
99                 else
100                         ctx_sched_job_requirement_create(uri.c_str(), false, &jobContext);
101         }
102
103         ~_context_trigger_rule_entry_s()
104         {
105                 ctx_sched_job_context_destroy(jobContext);
106         }
107 } _context_trigger_rule_entry_h;
108
109
110 namespace {
111         class Scheduler {
112         private:
113                 ctx_sched_h __scheduler;
114
115         public:
116                 Scheduler() : __scheduler(NULL) {}
117
118                 ~Scheduler()
119                 {
120                         ctx_sched_destroy(__scheduler);
121                 }
122
123                 ctx_sched_h get()
124                 {
125                         if (__scheduler == NULL)
126                                 ctx_sched_create(&__scheduler);
127
128                         return __scheduler;
129                 }
130         };
131 }
132
133 static OpType __getOpType(const char* opStr)
134 {
135         if (STR_EQ(opStr, CONTEXT_TRIGGER_EQUAL_TO))
136                 return OpType::EQ;
137
138         if (STR_EQ(opStr, CONTEXT_TRIGGER_NOT_EQUAL_TO))
139                 return OpType::NE;
140
141         if (STR_EQ(opStr, CONTEXT_TRIGGER_GREATER_THAN))
142                 return OpType::GT;
143
144         if (STR_EQ(opStr, CONTEXT_TRIGGER_GREATER_THAN_OR_EQUAL_TO))
145                 return OpType::GE;
146
147         if (STR_EQ(opStr, CONTEXT_TRIGGER_LESS_THAN))
148                 return OpType::LT;
149
150         if (STR_EQ(opStr, CONTEXT_TRIGGER_LESS_THAN_OR_EQUAL_TO))
151                 return OpType::LE;
152
153         return OpType::UNDEFINED;
154 }
155
156 static std::string __get_custom_uri(std::string name, std::string provider)
157 {
158         std::string uri("http://");
159         uri += provider + "/context/custom/" + name;
160         return uri;
161 }
162
163 static const char* __get_pkg_id()
164 {
165         static char pkgId[PKG_ID_LENGTH] = {0};
166
167         if (strlen(pkgId) > 0)
168                 return pkgId;
169
170         aul_app_get_pkgid_bypid(getpid(), pkgId, PKG_ID_LENGTH);
171
172         if (strlen(pkgId) > 0)
173                 return pkgId;
174
175         return NULL;
176 }
177
178 static ContextItem* __get_context_item(context_trigger_rule_entry_h entry)
179 {
180         ContextItem* contextItem = NULL;
181
182         if (entry->category == CATEGORY_EVENT)
183                 contextItem = new(std::nothrow) EventItem(entry->type);
184         else
185                 contextItem = new(std::nothrow) ConditionItem(entry->type);
186
187         if (!contextItem)
188                 _E_ALLOC;
189
190         return contextItem;
191 }
192
193 static Scheduler __scheduler;
194
195
196 EXPORT_API int context_trigger_add_rule(context_trigger_rule_h rule, int* rule_id)
197 {
198         INIT_SCHED;
199         ASSERT_NOT_NULL(rule && rule_id);
200
201         int err = ctx_sched_add_job(SCHED, rule->job, rule_id);
202
203         if (err == E_PARAM)
204                 return E_INV_RULE;
205
206         return err;
207 }
208
209 EXPORT_API int context_trigger_remove_rule(int rule_id)
210 {
211         INIT_SCHED;
212         if (rule_id <= 0)
213                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
214
215         int err = ctx_sched_remove_job(SCHED, rule_id);
216
217         if (err == E_RULE_ON)
218                 return CONTEXT_TRIGGER_ERROR_RULE_ENABLED;
219         else if (err == E_PARAM)
220                 return CONTEXT_TRIGGER_ERROR_RULE_NOT_EXIST;
221
222         return err;
223 }
224
225 EXPORT_API int context_trigger_enable_rule(int rule_id)
226 {
227         INIT_SCHED;
228         if (rule_id <= 0)
229                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
230
231         int err = ctx_sched_start_job(SCHED, rule_id);
232
233         if (err == E_PARAM)
234                 return CONTEXT_TRIGGER_ERROR_RULE_NOT_EXIST;
235
236         return err;
237 }
238
239 EXPORT_API int context_trigger_disable_rule(int rule_id)
240 {
241         INIT_SCHED;
242         if (rule_id <= 0)
243                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
244
245         int err = ctx_sched_stop_job(SCHED, rule_id);
246
247         if (err == E_PARAM)
248                 return CONTEXT_TRIGGER_ERROR_RULE_NOT_EXIST;
249
250         return err;
251 }
252
253 static bool __foreach_job_cb(ctx_sched_h scheduler, ctx_sched_job_h job, void *user_data)
254 {
255         auto jobs = static_cast<std::vector<ctx_sched_job_h>*>(user_data);
256         jobs->push_back(job);
257         return true;
258 }
259
260 EXPORT_API int context_trigger_get_own_rule_ids(int** enabled_rule_ids, int* enabled_rule_count, int** disabled_rule_ids, int* disabled_rule_count)
261 {
262         INIT_SCHED;
263         ASSERT_NOT_NULL(enabled_rule_ids && enabled_rule_count && disabled_rule_ids && disabled_rule_count);
264
265         std::vector<ctx_sched_job_h> jobs;
266
267         int err = ctx_sched_foreach_job(SCHED, __foreach_job_cb, &jobs);
268
269         if (err == E_NO_DATA) {
270                 *enabled_rule_ids = NULL;
271                 *enabled_rule_count = 0;
272                 *disabled_rule_ids = NULL;
273                 *disabled_rule_count = 0;
274                 return E_NONE;
275         }
276
277         IF_FAIL_RETURN(IS_SUCCESS(err), err);
278
279         std::vector<int> startedJobIds;
280         std::vector<int> stoppedJobIds;
281
282         for (auto& job : jobs) {
283                 bool started = false;
284                 int jobId = 0;
285
286                 ctx_sched_job_is_started(job, &started);
287                 ctx_sched_job_get_id(job, &jobId);
288                 ctx_sched_job_destroy(job);
289
290                 if (started)
291                         startedJobIds.push_back(jobId);
292                 else
293                         stoppedJobIds.push_back(jobId);
294         }
295
296         *enabled_rule_count = startedJobIds.size();
297
298         if (*enabled_rule_count > 0)
299                 *enabled_rule_ids = static_cast<int*>(g_memdup(startedJobIds.data(), startedJobIds.size() * sizeof(int)));
300         else
301                 *enabled_rule_ids = NULL;
302
303         *disabled_rule_count = stoppedJobIds.size();
304
305         if (*disabled_rule_count > 0)
306                 *disabled_rule_ids = static_cast<int*>(g_memdup(stoppedJobIds.data(), stoppedJobIds.size() * sizeof(int)));
307         else
308                 *disabled_rule_ids = NULL;
309
310         return E_NONE;
311 }
312
313 EXPORT_API int context_trigger_get_rule_by_id(int rule_id, context_trigger_rule_h* rule)
314 {
315         INIT_SCHED;
316         ASSERT_NOT_NULL(rule);
317         if (rule_id <= 0)
318                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
319
320         ctx_sched_job_h job = NULL;
321
322         int err = ctx_sched_get_job(SCHED, rule_id, &job);
323
324         if (err == E_PARAM)
325                 return CONTEXT_TRIGGER_ERROR_RULE_NOT_EXIST;
326         else if (err != E_NONE)
327                 return err;
328
329         *rule = new(std::nothrow) _context_trigger_rule_h(job);
330         if (*rule == NULL) {
331                 _E_ALLOC;
332                 ctx_sched_job_destroy(job);
333                 return E_NO_MEM;
334         }
335
336         return E_NONE;
337 }
338
339 EXPORT_API int context_trigger_rule_create(context_trigger_logical_type_e logical_type, context_trigger_rule_h* rule)
340 {
341         ASSERT_NOT_NULL(rule);
342         IF_FAIL_RETURN(logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION || logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION, E_PARAM);
343
344         *rule = new(std::nothrow) _context_trigger_rule_h();
345         ASSERT_ALLOC(*rule);
346         if ((*rule)->job == NULL) {
347                 _E_ALLOC;
348                 delete *rule;
349                 return E_NO_MEM;
350         }
351
352         ctx_sched_job_set_disjunction((*rule)->job, logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION);
353
354         return E_NONE;
355 }
356
357 EXPORT_API int context_trigger_rule_destroy(context_trigger_rule_h rule)
358 {
359         INIT_SCHED;
360         ASSERT_NOT_NULL(rule);
361
362         delete rule;
363
364         return E_NONE;
365 }
366
367 static int __set_event(context_trigger_rule_h rule, context_trigger_rule_entry_h event)
368 {
369         IF_FAIL_RETURN(event->jobContext, E_PARAM);
370         IF_FAIL_RETURN_TAG(!rule->hasEvent, E_INV_RULE, _E, "The rule already has the event");
371
372         EventItem contextItem(event->type);
373         IF_FAIL_RETURN_TAG(contextItem.isValid(event->jobContext), E_INV_RULE, _E, "Incomplete event");
374
375         ctx_sched_job_context_h dup = ctx_sched_job_context_duplicate(event->jobContext);
376         ASSERT_ALLOC(dup);
377
378         int err = ctx_sched_job_add_trigger(rule->job, dup);
379         if (IS_FAILED(err)) {
380                 ctx_sched_job_context_destroy(dup);
381                 return err;
382         }
383
384         rule->hasEvent = true;
385
386         return E_NONE;
387 }
388
389 static int __add_condition(context_trigger_rule_h rule, context_trigger_rule_entry_h condition)
390 {
391         IF_FAIL_RETURN(condition->jobContext, E_PARAM);
392
393         bool exist = (rule->conditions.find(condition->uri) != rule->conditions.end());
394         IF_FAIL_RETURN_TAG(!exist, E_INV_RULE, _E, "The same condition exists");
395
396         ConditionItem contextItem(condition->type);
397         IF_FAIL_RETURN_TAG(contextItem.isValid(condition->jobContext), E_INV_RULE, _E, "Incomplete condition");
398
399         ctx_sched_job_context_h dup = ctx_sched_job_context_duplicate(condition->jobContext);
400         ASSERT_ALLOC(dup);
401
402         int err = ctx_sched_job_add_requirement(rule->job, dup);
403         if (IS_FAILED(err)) {
404                 ctx_sched_job_context_destroy(dup);
405                 return err;
406         }
407
408         return E_NONE;
409 }
410
411 EXPORT_API int context_trigger_rule_add_entry(context_trigger_rule_h rule, context_trigger_rule_entry_h entry)
412 {
413         ASSERT_NOT_NULL(rule && entry);
414         IF_FAIL_RETURN_TAG(!rule->readOnly, E_PARAM,
415                         _E, "A rule acquired by context_trigger_get_rule_by_id() is not allowed to be modified.");
416
417         if (entry->category == CATEGORY_EVENT)
418                 return __set_event(rule, entry);
419         else
420                 return __add_condition(rule, entry);
421 }
422
423 static bool __is_call_operation(app_control_h ctrl)
424 {
425         char *op = NULL;
426         int err = app_control_get_operation(ctrl, &op);
427         IF_FAIL_RETURN_TAG(err == APP_CONTROL_ERROR_NONE, false, _E, "Getting operation of app control failed");
428
429         bool ret = STR_EQ(op, APP_CONTROL_OPERATION_CALL);
430         g_free(op);
431
432         return ret;
433 }
434
435 static bool __is_ui_app(app_control_h ctrl)
436 {
437         char* appId = NULL;
438         int err = app_control_get_app_id(ctrl, &appId);
439
440         IF_FAIL_RETURN_TAG(err == E_NONE, false, _E, "Failed to get the app id");
441
442         pkgmgrinfo_appinfo_h appInfo;
443         err = pkgmgrinfo_appinfo_get_usr_appinfo(appId, getuid(), &appInfo);
444         g_free(appId);
445
446         IF_FAIL_RETURN_TAG(err == PMINFO_R_OK, false, _E, "No such app");
447
448         char *appType = NULL;
449         pkgmgrinfo_appinfo_get_component_type(appInfo, &appType);
450
451         bool ret = STR_EQ(appType, "uiapp");
452
453         pkgmgrinfo_appinfo_destroy_appinfo(appInfo);
454
455         return ret;
456 }
457
458 EXPORT_API int context_trigger_rule_set_action_app_control(context_trigger_rule_h rule, app_control_h app_control)
459 {
460         INIT_SCHED;
461         ASSERT_NOT_NULL(rule && app_control);
462
463         if (!PrivilegeChecker::hasPrivilege("http://tizen.org/privilege/appmanager.launch")) {
464                 _E("Privilege denied");
465                 return E_ACCESS;
466         }
467
468         if (__is_call_operation(app_control) &&
469                         !PrivilegeChecker::hasPrivilege("http://tizen.org/privilege/call")) {
470                 _E("Privilege denied");
471                 return E_ACCESS;
472         }
473
474         IF_FAIL_RETURN_TAG(!rule->hasAction, E_INV_RULE, _E, "The rule has an action");
475         IF_FAIL_RETURN_TAG(__is_ui_app(app_control), E_INV_RULE, _E, "Invalid app-control");
476
477         bundle* bn = NULL;
478         app_control_export_as_bundle(app_control, &bn);
479         IF_FAIL_RETURN_TAG(bn, E_PARAM, _E, "Converting failed");
480
481         int ret = ctx_sched_job_set_app_control(rule->job, bn);
482
483         if (ret == E_NONE)
484                 rule->hasAction = true;
485
486         return ret;
487 }
488
489 static bool __is_valid_app_control(app_control_h ctrl)
490 {
491         if (!ctrl) return true;
492
493         char* appId = NULL;
494         int err = app_control_get_app_id(ctrl, &appId);
495         IF_FAIL_RETURN_TAG(err == E_NONE, false, _E, "Failed to get the app id");
496
497         pkgmgrinfo_appinfo_h appInfo;
498         err = pkgmgrinfo_appinfo_get_usr_appinfo(appId, getuid(), &appInfo);
499         g_free(appId);
500         IF_FAIL_RETURN_TAG(err == PMINFO_R_OK, false, _E, "No such app");
501
502         pkgmgrinfo_appinfo_destroy_appinfo(appInfo);
503
504         return true;
505 }
506
507 EXPORT_API int context_trigger_rule_set_action_notification(context_trigger_rule_h rule,
508                 const char* title, const char* content, const char* icon_path, app_control_h app_control)
509 {
510         INIT_SCHED;
511         ASSERT_NOT_NULL(rule && title && content);
512
513         if (!PrivilegeChecker::hasPrivilege("http://tizen.org/privilege/notification")) {
514                 _E("Privilege denied");
515                 return E_ACCESS;
516         }
517
518         IF_FAIL_RETURN_TAG(!rule->hasAction, E_INV_RULE, _E, "The rule has an action");
519         IF_FAIL_RETURN_TAG(__is_valid_app_control(app_control), E_INV_RULE, _E, "Invalid app-control");
520
521         bundle* bn = NULL;
522         if (app_control) {
523                 app_control_export_as_bundle(app_control, &bn);
524                 IF_FAIL_RETURN_TAG(bn, E_PARAM, _E, "Converting failed");
525         }
526
527         int ret = ctx_sched_job_set_notification(rule->job, title, content, icon_path, bn);
528
529         if (ret == E_NONE)
530                 rule->hasAction = true;
531
532         return ret;
533 }
534
535 //LCOV_EXCL_START
536 EXPORT_API int context_trigger_rule_set_action_dbus_call(context_trigger_rule_h rule,
537                 const char *bus_name, const char *object_path, const char *interface_name, const char *method_name, GVariant *param)
538 {
539         ASSERT_NOT_NULL(rule && bus_name && object_path && interface_name && method_name);
540         IF_FAIL_RETURN_TAG(!rule->hasAction, E_INV_RULE, _E, "The rule has an action");
541
542         int ret = ctx_sched_job_set_dbus(rule->job, bus_name, object_path, interface_name, method_name, param);
543
544         if (ret == E_NONE)
545                 rule->hasAction = true;
546
547         return ret;
548 }
549 //LCOV_EXCL_STOP
550
551 EXPORT_API int context_trigger_rule_set_description(context_trigger_rule_h rule, const char* description)
552 {
553         ASSERT_NOT_NULL(rule && description);
554         IF_FAIL_RETURN(strlen(description) > 0, E_PARAM);
555
556         return ctx_sched_job_set_user_data(rule->job, description, strlen(description) + 1);
557 }
558
559 EXPORT_API int context_trigger_rule_get_description(context_trigger_rule_h rule, char** description)
560 {
561         ASSERT_NOT_NULL(rule && description);
562
563         char* val = NULL;
564         size_t len = 0;
565         int err = ctx_sched_job_get_user_data(rule->job, &val, &len);
566         IF_FAIL_RETURN(IS_SUCCESS(err), err);
567         IF_FAIL_RETURN(val, E_NONE);
568
569         *description = val;
570
571         return E_NONE;
572 }
573
574 EXPORT_API int context_trigger_rule_event_create(context_trigger_event_e event_item, context_trigger_logical_type_e logical_type, context_trigger_rule_entry_h* entry)
575 {
576         INIT_SCHED;
577         ASSERT_NOT_NULL(entry);
578         IF_FAIL_RETURN(logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION || logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION, E_PARAM);
579
580         EventItem contextItem(event_item);
581         IF_FAIL_RETURN_TAG(contextItem.getUri(), E_PARAM, _E, "Unknown Event: %d", event_item);
582
583         if (contextItem.deprecated())
584                 _DEPRECATED_EVENT(event_item);
585
586         IF_FAIL_RETURN_TAG(contextItem.allowed(), E_ACCESS, _E, "Privilege denied");
587
588         bool supported = false;
589         ctx_sched_job_trigger_is_supported(SCHED, contextItem.getUri(), &supported);
590         IF_FAIL_RETURN_TAG(supported, E_SUPPORT, _E, "Event-%d is not supported", event_item);
591
592         *entry = new(std::nothrow) _context_trigger_rule_entry_h(CATEGORY_EVENT, event_item, contextItem.getUri());
593         ASSERT_ALLOC(*entry);
594
595         ctx_sched_job_context_set_disjunction((*entry)->jobContext, (logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION));
596
597         return E_NONE;
598 }
599
600 static bool __is_valid_pkg_id(const char* pkgId)
601 {
602         IF_FAIL_RETURN(pkgId, false);
603
604         pkgmgrinfo_pkginfo_h pkgInfo;
605         int err = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgId, getuid(), &pkgInfo);
606         pkgmgrinfo_pkginfo_destroy_pkginfo(pkgInfo);
607
608         return (PMINFO_R_OK == err);
609 }
610
611 EXPORT_API int context_trigger_rule_custom_event_create(const char* event_item, const char* provider, context_trigger_logical_type_e logical_type, context_trigger_rule_entry_h* entry)
612 {
613         INIT_SCHED;
614         ASSERT_NOT_NULL(event_item && provider && entry);
615         IF_FAIL_RETURN_TAG(__is_valid_pkg_id(provider), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "No such package");
616
617         std::string uri = __get_custom_uri(event_item, provider);
618
619         bool registered = false;
620         ctx_sched_custom_is_registered(SCHED, uri.c_str(), provider, &registered);
621         IF_FAIL_RETURN_TAG(registered, E_SUPPORT, _W, "%s is not registered yet", event_item);
622
623         *entry = new(std::nothrow) _context_trigger_rule_entry_h(CATEGORY_EVENT, 0, uri.c_str());
624         ASSERT_ALLOC(*entry);
625
626         return E_NONE;
627 }
628
629 EXPORT_API int context_trigger_rule_event_is_supported(context_trigger_event_e event_item, bool* supported)
630 {
631         INIT_SCHED;
632         ASSERT_NOT_NULL(supported);
633
634         EventItem contextItem(event_item);
635         IF_FAIL_RETURN_TAG(contextItem.getUri(), E_PARAM, _E, "Unknown Event: %d", event_item);
636
637         return ctx_sched_job_trigger_is_supported(SCHED, contextItem.getUri(), supported);
638 }
639
640 EXPORT_API int context_trigger_rule_condition_create(context_trigger_condition_e condition_item, context_trigger_logical_type_e logical_type, context_trigger_rule_entry_h* entry)
641 {
642         INIT_SCHED;
643         ASSERT_NOT_NULL(entry);
644         IF_FAIL_RETURN(logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION || logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION, E_PARAM);
645
646         ConditionItem contextItem(condition_item);
647         IF_FAIL_RETURN_TAG(contextItem.getUri(), E_PARAM, _E, "Unknown Condition: %d", condition_item);
648
649         if (contextItem.deprecated())
650                 _DEPRECATED_COND(condition_item);
651
652         IF_FAIL_RETURN_TAG(contextItem.allowed(), E_ACCESS, _E, "Privilege denied");
653
654         bool supported = false;
655         ctx_sched_job_requirement_is_supported(SCHED, contextItem.getUri(), &supported);
656         IF_FAIL_RETURN_TAG(supported, E_SUPPORT, _E, "Condition-%d is not supported", condition_item);
657
658         *entry = new(std::nothrow) _context_trigger_rule_entry_h(CATEGORY_CONDITION, condition_item, contextItem.getUri());
659         ASSERT_ALLOC(*entry);
660
661         ctx_sched_job_context_set_disjunction((*entry)->jobContext, (logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION));
662
663         return E_NONE;
664 }
665
666 EXPORT_API int context_trigger_rule_custom_condition_create(const char* condition_item, const char* provider, context_trigger_logical_type_e logical_type, context_trigger_rule_entry_h* entry)
667 {
668         INIT_SCHED;
669         ASSERT_NOT_NULL(condition_item && provider && entry);
670         IF_FAIL_RETURN(logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION || logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION, E_PARAM);
671         IF_FAIL_RETURN_TAG(__is_valid_pkg_id(provider), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "No such package");
672
673         std::string uri = __get_custom_uri(condition_item, provider);
674
675         bool registered = false;
676         ctx_sched_custom_is_registered(SCHED, uri.c_str(), provider, &registered);
677         IF_FAIL_RETURN_TAG(registered, E_SUPPORT, _W, "%s is not registered yet", condition_item);
678
679         *entry = new(std::nothrow) _context_trigger_rule_entry_h(CATEGORY_CONDITION, 0, uri.c_str());
680         ASSERT_ALLOC(*entry);
681
682         return E_NONE;
683 }
684
685 EXPORT_API int context_trigger_rule_condition_is_supported(context_trigger_condition_e condition_item, bool* supported)
686 {
687         INIT_SCHED;
688         ASSERT_NOT_NULL(supported);
689
690         ConditionItem contextItem(condition_item);
691         IF_FAIL_RETURN_TAG(contextItem.getUri(), E_PARAM, _E, "Unknown Condition: %d", condition_item);
692
693         return ctx_sched_job_requirement_is_supported(SCHED, contextItem.getUri(), supported);
694 }
695
696 EXPORT_API int context_trigger_rule_entry_destroy(context_trigger_rule_entry_h entry)
697 {
698         INIT_SCHED;
699         ASSERT_NOT_NULL(entry);
700
701         delete entry;
702
703         return E_NONE;
704 }
705
706 EXPORT_API int context_trigger_rule_entry_add_option_int(context_trigger_rule_entry_h entry, const char* option_key, int value)
707 {
708         ASSERT_NOT_NULL(entry && option_key);
709
710         ContextItem* contextItem = __get_context_item(entry);
711         IF_FAIL_RETURN(contextItem, E_FAILED);
712
713         bool valid = contextItem->isValid(option_key, value);
714         delete contextItem;
715         IF_FAIL_RETURN(valid, E_INV_RULE);
716
717         ctx_sched_job_context_prepare_attribute_int(entry->jobContext, option_key);
718         return ctx_sched_job_context_attribute_add_eq_int(entry->jobContext, option_key, value);
719 }
720
721 EXPORT_API int context_trigger_rule_entry_add_option_string(context_trigger_rule_entry_h entry, const char* option_key, const char* value)
722 {
723         _DEPRECATED_FUNC;
724         ASSERT_NOT_NULL(entry && option_key && value);
725
726         ContextItem* contextItem = __get_context_item(entry);
727         IF_FAIL_RETURN(contextItem, E_FAILED);
728
729         bool valid = contextItem->isValid(option_key, value);
730         delete contextItem;
731         IF_FAIL_RETURN(valid, E_INV_RULE);
732
733         ctx_sched_job_context_prepare_attribute_str(entry->jobContext, option_key);
734         return ctx_sched_job_context_attribute_add_eq_str(entry->jobContext, option_key, value);
735 }
736
737 EXPORT_API int context_trigger_rule_entry_add_option(context_trigger_rule_entry_h entry, const char* option_key, const char* event_data_key)
738 {
739         _DEPRECATED_FUNC;
740         ASSERT_NOT_NULL(entry && option_key && event_data_key);
741         IF_FAIL_RETURN(entry->category == CATEGORY_CONDITION, E_INV_RULE);
742
743         ConditionItem contextItem(entry->type);
744         IF_FAIL_RETURN(contextItem.isValid(option_key), E_INV_RULE);
745
746         ctx_sched_job_context_prepare_attribute_str(entry->jobContext, option_key);
747         return ctx_sched_job_context_attribute_add_eq_str(entry->jobContext, option_key, event_data_key);
748 }
749
750 static bool __is_member_key(context_trigger_rule_entry_h entry, const char* key)
751 {
752         if (entry->conjunctionKeys.find(key) != entry->conjunctionKeys.end() ||
753                         entry->disjunctionKeys.find(key) != entry->disjunctionKeys.end()) {
754                 _D("'%s' found", key);
755                 return true;
756         }
757
758         _D("'%s' not found", key);
759         return false;
760 }
761
762 EXPORT_API int context_trigger_rule_entry_add_key(context_trigger_rule_entry_h entry, context_trigger_logical_type_e logical_type, const char* key)
763 {
764         ASSERT_NOT_NULL(entry && key);
765         IF_FAIL_RETURN(logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION || logical_type == CONTEXT_TRIGGER_LOGICAL_DISJUNCTION, E_PARAM);
766
767         ContextItem* contextItem = __get_context_item(entry);
768         IF_FAIL_RETURN(contextItem, E_FAILED);
769
770         bool valid = contextItem->isValid(key);
771         delete contextItem;
772         IF_FAIL_RETURN_TAG(valid, E_INV_RULE, _E, "Invalid parameter");
773         IF_FAIL_RETURN(!__is_member_key(entry, key), E_INV_RULE);
774
775         if (logical_type == CONTEXT_TRIGGER_LOGICAL_CONJUNCTION)
776                 entry->conjunctionKeys.insert(key);
777         else
778                 entry->disjunctionKeys.insert(key);
779
780         return E_NONE;
781 }
782
783 EXPORT_API int context_trigger_rule_entry_add_comparison(context_trigger_rule_entry_h entry, const char* key, const char* op, const char* event_data_key)
784 {
785         _DEPRECATED_FUNC;
786         ASSERT_NOT_NULL(entry && key && op && event_data_key);
787         IF_FAIL_RETURN(entry->category == CATEGORY_CONDITION, E_INV_RULE);
788
789         ConditionItem contextItem(entry->type);
790         IF_FAIL_RETURN(contextItem.isValid(key), E_INV_RULE);
791         IF_FAIL_RETURN(__is_member_key(entry, key), E_NO_DATA);
792
793         ctx_sched_job_context_prepare_attribute_str(entry->jobContext, key);
794
795         if (__getOpType(op) == OpType::EQ)
796                 return ctx_sched_job_context_attribute_add_eq_str(entry->jobContext, key, event_data_key);
797
798         if (__getOpType(op) == OpType::NE)
799                 return ctx_sched_job_context_attribute_add_ne_str(entry->jobContext, key, event_data_key);
800
801         _E("Invalid operator");
802         return E_INV_RULE;
803 }
804
805 EXPORT_API int context_trigger_rule_entry_add_comparison_int(context_trigger_rule_entry_h entry, const char* key, const char* op, int value)
806 {
807         ASSERT_NOT_NULL(entry && key && op);
808
809         ContextItem* contextItem = __get_context_item(entry);
810         IF_FAIL_RETURN(contextItem, E_FAILED);
811
812         bool valid = contextItem->isValid(key, value);
813         delete contextItem;
814         IF_FAIL_RETURN_TAG(valid, E_INV_RULE, _E, "Invalid parameter");
815         IF_FAIL_RETURN(__is_member_key(entry, key), E_NO_DATA);
816
817         ctx_sched_job_context_prepare_attribute_int(entry->jobContext, key);
818
819         if (__getOpType(op) == OpType::EQ)
820                 return ctx_sched_job_context_attribute_add_eq_int(entry->jobContext, key, value);
821
822         if (__getOpType(op) == OpType::NE)
823                 return ctx_sched_job_context_attribute_add_ne_int(entry->jobContext, key, value);
824
825         //TODO: Consider logical disjunction
826         if (__getOpType(op) == OpType::GT)
827                 return ctx_sched_job_context_attribute_set_gt_int(entry->jobContext, key, value);
828
829         if (__getOpType(op) == OpType::GE)
830                 return ctx_sched_job_context_attribute_set_ge_int(entry->jobContext, key, value);
831
832         if (__getOpType(op) == OpType::LT)
833                 return ctx_sched_job_context_attribute_set_lt_int(entry->jobContext, key, value);
834
835         if (__getOpType(op) == OpType::LE)
836                 return ctx_sched_job_context_attribute_set_le_int(entry->jobContext, key, value);
837
838         _E("Invalid operator");
839         return E_INV_RULE;
840 }
841
842 EXPORT_API int context_trigger_rule_entry_add_comparison_string(context_trigger_rule_entry_h entry, const char* key, const char* op, const char* value)
843 {
844         ASSERT_NOT_NULL(entry && key && op && value);
845
846         ContextItem* contextItem = __get_context_item(entry);
847         IF_FAIL_RETURN(contextItem, E_FAILED);
848
849         bool valid = contextItem->isValid(key, value);
850         delete contextItem;
851         IF_FAIL_RETURN_TAG(valid, E_INV_RULE, _E, "Invalid parameter");
852         IF_FAIL_RETURN(__is_member_key(entry, key), E_NO_DATA);
853
854         ctx_sched_job_context_prepare_attribute_str(entry->jobContext, key);
855
856         if (__getOpType(op) == OpType::EQ)
857                 return ctx_sched_job_context_attribute_add_eq_str(entry->jobContext, key, value);
858
859         if (__getOpType(op) == OpType::NE)
860                 return ctx_sched_job_context_attribute_add_ne_str(entry->jobContext, key, value);
861
862         _E("Invalid operator");
863         return E_INV_RULE;
864 }
865
866 EXPORT_API int context_trigger_custom_register(const char* name, const char* attr_template)
867 {
868         INIT_SCHED;
869         ASSERT_NOT_NULL(name && attr_template);
870
871         //TODO: Is it allowed to overwrite a template?
872         CustomTemplate::remove(name);
873
874         bool success = CustomTemplate::add(name, attr_template);
875         IF_FAIL_RETURN_TAG(success, CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid template");
876
877         const char* pkgId = __get_pkg_id();
878         IF_FAIL_RETURN_TAG(pkgId, E_SUPPORT, _E, "PkgId is required");
879
880         std::string uri = __get_custom_uri(name, pkgId);
881
882         int err = ctx_sched_custom_register(SCHED, uri.c_str());
883         IF_FAIL_RETURN(err == E_NONE, E_FAILED);
884
885         return E_NONE;
886 }
887
888 EXPORT_API int context_trigger_custom_unregister(const char* name)
889 {
890         INIT_SCHED;
891         ASSERT_NOT_NULL(name);
892
893         CustomTemplate::remove(name);
894
895         const char* pkgId = __get_pkg_id();
896         IF_FAIL_RETURN_TAG(pkgId, E_SUPPORT, _E, "PkgId is required");
897
898         std::string uri = __get_custom_uri(name, pkgId);
899
900         ctx_sched_custom_unregister(SCHED, uri.c_str());
901
902         return E_NONE;
903 }
904
905 EXPORT_API int context_trigger_custom_publish(const char* name, const char* fact)
906 {
907         INIT_SCHED;
908         ASSERT_NOT_NULL(name && fact);
909
910         CustomTemplate* customTemplate = CustomTemplate::get(name);
911         IF_FAIL_RETURN_TAG(customTemplate, E_PARAM, _E, "Unknown custom name");
912         IF_FAIL_RETURN(customTemplate->match(fact), CONTEXT_TRIGGER_ERROR_INVALID_DATA);
913
914         const char* pkgId = __get_pkg_id();
915         IF_FAIL_RETURN_TAG(pkgId, E_SUPPORT, _E, "PkgId is required");
916
917         std::string uri = __get_custom_uri(name, pkgId);
918
919         return ctx_sched_publish_context_json(SCHED, uri.c_str(), fact);
920 }