2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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.
20 #include <types_internal.h>
24 #include <app_control.h>
26 #include <app_control_internal.h>
27 #include <device/display.h>
28 #include <notification.h>
29 #include <notification_internal.h>
30 #include <runtime_info.h>
31 #include <system_settings.h>
32 #include <context_trigger_types_internal.h>
33 #include <context_trigger.h>
35 #include "../dbus_server_impl.h"
36 #include <app_manager.h>
37 #include "fact_reader.h"
38 #include "rule_manager.h"
39 #include "script_generator.h"
42 #define RULE_TABLE "context_trigger_rule"
43 #define EVENT_TABLE "context_trigger_event"
44 #define CONDITION_TABLE "context_trigger_condition"
45 #define TEMPLATE_TABLE "context_trigger_template"
47 #define RULE_TABLE_COLUMNS "enabled INTEGER DEFAULT 0 NOT NULL, creator TEXT DEFAULT '' NOT NULL, creator_app_id TEXT DEFAULT '' NOT NULL, description TEXT DEFAULT '', details TEXT DEFAULT '' NOT NULL"
48 #define EVENT_TABLE_COLUMNS "rule_id INTEGER references context_trigger_rule(row_id) ON DELETE CASCADE NOT NULL, name TEXT DEFAULT '' NOT NULL, instance_name TEXT DEFAULT ''"
49 #define CONDITION_TABLE_COLUMNS "rule_id INTEGER references context_trigger_rule(row_id) ON DELETE CASCADE NOT NULL, name TEXT DEFAULT '' NOT NULL, option TEXT DEFAULT '', instance_name TEXT DEFAULT ''"
50 #define CREATE_TEMPLATE_TABLE "CREATE TABLE IF NOT EXISTS context_trigger_template (name TEXT DEFAULT '' NOT NULL PRIMARY KEY, operation INTEGER DEFAULT 3 NOT NULL, attributes TEXT DEFAULT '' NOT NULL, options TEXT DEFAULT '' NOT NULL)"
51 #define QUERY_TEMPLATE_TABLE "SELECT name, operation, attributes, options FROM context_trigger_template"
52 #define FOREIGN_KEYS_ON "PRAGMA foreign_keys = ON"
53 #define DELETE_RULE_STATEMENT "DELETE FROM 'context_trigger_rule' where row_id = "
54 #define UPDATE_RULE_ENABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 1 WHERE row_id = "
55 #define UPDATE_RULE_DISABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 0 WHERE row_id = "
56 #define QUERY_NAME_INSTANCE_NAME_AND_ATTRIBUTES_BY_RULE_ID_STATEMENT "SELECT context_trigger_condition.name, instance_name, attributes FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id = "
57 #define QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT "SELECT DISTINCT context_trigger_condition.name, instance_name, option, attributes, options FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id IN (SELECT row_id FROM context_trigger_rule WHERE enabled = 1 AND row_id IN (SELECT rule_id FROM context_trigger_event WHERE context_trigger_event.instance_name = '"
58 #define QUERY_RULE_BY_RULE_ID "SELECT details FROM context_trigger_rule WHERE row_id = "
59 #define QUERY_EVENT_TEMPLATE_BY_RULE_ID "SELECT name, attributes, options FROM context_trigger_template WHERE name IN (SELECT name FROM context_trigger_event WHERE rule_id = "
60 #define QUERY_CONDITION_BY_RULE_ID "SELECT name, option FROM context_trigger_condition WHERE rule_id = "
62 #define INSTANCE_NAME_DELIMITER "/"
63 #define EVENT_KEY_PREFIX "?"
65 static ctx::context_trigger* trigger = NULL;
66 static int enb_rule_cnt = 0;
68 static int string_to_int(std::string str)
71 std::istringstream convert(str);
79 static std::string int_to_string(int i)
81 std::ostringstream convert;
83 std::string str = convert.str();
87 static bool convert_str_to_json(ctx::json* val, const char* path, const char* key)
90 IF_FAIL_RETURN(val, false);
93 IF_FAIL_RETURN(val->get(path, key, &buf), false);
96 IF_FAIL_RETURN(val->set(path, key, temp), false);
101 ctx::rule_manager::rule_manager()
105 ctx::rule_manager::~rule_manager()
110 bool ctx::rule_manager::init(ctx::context_trigger* tr, ctx::fact_reader* fr)
117 ret = c_monitor.init(fr, tr);
118 IF_FAIL_RETURN_TAG(ret, false, _E, "Context monitor initialization failed");
120 // Create tables into db (rule, event, condition, action, template)
121 ret = db_manager::create_table(1, RULE_TABLE, RULE_TABLE_COLUMNS, NULL, NULL);
122 IF_FAIL_RETURN_TAG(ret, false, _E, "Create rule table failed");
124 ret = db_manager::create_table(2, EVENT_TABLE, EVENT_TABLE_COLUMNS, NULL, NULL);
125 IF_FAIL_RETURN_TAG(ret, false, _E, "Create event table failed");
127 ret = db_manager::create_table(3, CONDITION_TABLE, CONDITION_TABLE_COLUMNS, NULL, NULL);
128 IF_FAIL_RETURN_TAG(ret, false, _E, "Create condition table failed");
130 ret = db_manager::execute(4, CREATE_TEMPLATE_TABLE, NULL);
131 IF_FAIL_RETURN_TAG(ret, false, _E, "Create template table failed");
134 std::vector<json> record;
135 ret = db_manager::execute_sync(FOREIGN_KEYS_ON, &record);
136 IF_FAIL_RETURN_TAG(ret, false, _E, "Foreign keys on failed");
140 if (get_uninstalled_app() > 0) {
141 error = clear_rule_of_uninstalled_app(true);
142 IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Failed to remove uninstalled apps' rules while initialization");
144 ret = reenable_rule();
149 void ctx::rule_manager::apply_templates(ctx::fact_reader *fr)
153 ctx::json attributes;
155 std::string q_update;
156 std::string q_insert = "INSERT OR IGNORE INTO context_trigger_template (name, operation, attributes, options) VALUES";
158 while (fr->get_fact_definition(subject, operation, attributes, options)) {
159 _D("Subject: %s, Ops: %d", subject.c_str(), operation);
160 _J("Attr", attributes);
163 q_update += "UPDATE context_trigger_template SET operation=" + int_to_string(operation)
164 + ", attributes='" + attributes.str() + "', options='" + options.str() + "' WHERE name='" + subject + "';";
166 q_insert += " ('" + subject + "', " + int_to_string(operation) + ", '" + attributes.str() + "', '" + options.str() + "'),";
169 q_insert.erase(q_insert.end() - 1, q_insert.end());
172 bool ret = db_manager::execute(5, q_update.c_str(), NULL);
174 _E("Update item definition failed");
176 ret = db_manager::execute(6, q_insert.c_str(), NULL);
177 IF_FAIL_VOID_TAG(ret, _E, "Insert item definition failed");
180 int ctx::rule_manager::get_uninstalled_app(void)
182 // Return number of uninstalled apps
183 std::string q1 = "SELECT DISTINCT creator_app_id FROM context_trigger_rule";
185 std::vector<json> record;
186 bool ret = db_manager::execute_sync(q1.c_str(), &record);
187 IF_FAIL_RETURN_TAG(ret, -1, _E, "Query creators of registered rules failed");
189 std::vector<json>::iterator vec_end = record.end();
190 for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
191 ctx::json elem = *vec_pos;
193 elem.get(NULL, "creator_app_id", &app_id);
195 if (is_uninstalled_package(app_id)) {
196 uninstalled_apps.insert(app_id);
200 return uninstalled_apps.size();
203 bool ctx::rule_manager::is_uninstalled_package(std::string app_id)
205 IF_FAIL_RETURN_TAG(!app_id.empty(), false, _D, "Empty app id");
208 int error = app_manager_get_app_info(app_id.c_str(), &app_info);
210 if (error == APP_MANAGER_ERROR_NONE) {
211 app_info_destroy(app_info);
212 } else if (error == APP_MANAGER_ERROR_NO_SUCH_APP) {
213 // Uninstalled app found
214 _D("Uninstalled app found: %s", app_id.c_str());
217 _E("Get app info(%s) failed: %d", app_id.c_str(), error);
223 int ctx::rule_manager::clear_rule_of_uninstalled_app(bool is_init)
225 if (uninstalled_apps.size() <= 0) {
232 _D("Clear uninstalled apps' rule started");
234 std::string creator_list = "(";
235 std::set<std::string>::iterator it = uninstalled_apps.begin();
236 creator_list += "creator_app_id = '" + *it + "'";
238 for (; it != uninstalled_apps.end(); ++it) {
239 creator_list += " OR creator_app_id = '" + *it + "'";
243 // After event received, disable all the enabled rules of uninstalled apps
245 std::string q1 = "SELECT row_id, details FROM context_trigger_rule WHERE enabled = 1 and (";
249 std::vector<json> record;
250 ret = db_manager::execute_sync(q1.c_str(), &record);
251 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query enabled rules of uninstalled apps failed");
253 std::vector<json>::iterator vec_end = record.end();
254 for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
255 ctx::json elem = *vec_pos;
256 error = disable_uninstalled_rule(elem);
257 IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to disable rules" );
259 _D("Uninstalled apps' rules are disabled");
262 // Delete rules of uninstalled apps from DB
263 std::string q2 = "DELETE FROM context_trigger_rule WHERE " + creator_list;
264 std::vector<json> dummy;
265 ret = db_manager::execute_sync(q2.c_str(), &dummy);
266 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed");
267 _D("Uninstalled apps's rule are deleted from db");
269 uninstalled_apps.clear();
274 int ctx::rule_manager::disable_uninstalled_rule(ctx::json& rule_info)
280 rule_info.get(NULL, "row_id", &rule_id);
282 // For event with options
284 rule_info.get(NULL, "details", &r1);
287 rule.get(NULL, CT_RULE_EVENT, &event);
289 event.get(NULL, CT_RULE_EVENT_ITEM, &ename);
292 error = c_monitor.unsubscribe(rule_id, ename, event);
293 IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Failed to unsubscribe %s of rule%d: %d", ename.c_str(), rule_id, error);
295 // Undef rule in clips
296 std::string id_str = int_to_string(rule_id);
297 std::string script = script_generator::generate_undefrule(id_str);
298 error = clips_h->route_string_command(script);
299 IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Failed to undefine rule%d: %d", rule_id, error);
301 // Remove condition instances
302 std::string q3 = "SELECT name, instance_name FROM context_trigger_condition WHERE rule_id = ";
304 std::vector<json> name_record;
305 ret = db_manager::execute_sync(q3.c_str(), &name_record);
306 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Failed to query condition table of rule%d failed: %d", rule_id, error);
308 std::vector<json>::iterator vec_end = name_record.end();
309 for (std::vector<json>::iterator vec_pos = name_record.begin(); vec_pos != vec_end; ++vec_pos) {
310 ctx::json elem = *vec_pos;
314 elem.get(NULL, "name", &cname);
315 elem.get(NULL, "instance_name", &ciname);
317 if (cname.compare(ciname) != 0) {
318 cond_cnt_map[ciname]--;
320 if (cond_cnt_map[ciname] == 0) {
321 error = clips_h->unmake_instance(ciname);
322 IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to unmake instance %s of rule%d: %d", ciname.c_str(), rule_id, error);
324 cond_cnt_map.erase(ciname);
329 if (--enb_rule_cnt <= 0) {
336 bool ctx::rule_manager::initialize_clips(void)
339 _D("CLIPS handler already initialized");
343 clips_h = new(std::nothrow) clips_handler(this);
344 IF_FAIL_RETURN_TAG(clips_h, false, _E, "CLIPS handler initialization failed");
346 // Load all templates from DB
347 std::vector<json> record;
348 bool ret = db_manager::execute_sync(QUERY_TEMPLATE_TABLE, &record);
349 IF_FAIL_RETURN_TAG(ret, false, _E, "Query template table failed");
351 // Make scripts for deftemplate, defclass, make-instance and load them to clips
352 std::vector<json>::iterator vec_end = record.end();
353 for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
354 ctx::json tmpl = *vec_pos;
355 convert_str_to_json(&tmpl, NULL, "attributes");
356 convert_str_to_json(&tmpl, NULL, "options");
358 std::string deftemplate_str = script_generator::generate_deftemplate(tmpl);
359 int error = clips_h->define_template(deftemplate_str);
360 IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Deftemplate failed");
362 std::string defclass_str = script_generator::generate_defclass(tmpl);
363 error = clips_h->define_class(defclass_str);
364 IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Defclass failed");
366 std::string makeinstance_str = script_generator::generate_makeinstance(tmpl);
367 error = clips_h->make_instance(makeinstance_str);
368 IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Makeinstance failed");
371 _D(YELLOW("Deftemplate, Defclass, Make-instance completed"));
375 void ctx::rule_manager::destroy_clips(void)
381 bool ctx::rule_manager::reenable_rule(void)
384 std::string q = "SELECT row_id FROM context_trigger_rule where enabled = 1";
386 std::vector<json> record;
387 bool ret = db_manager::execute_sync(q.c_str(), &record);
388 IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_ids of enabled rules failed");
390 std::vector<json>::iterator vec_end = record.end();
391 for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
392 ctx::json elem = *vec_pos;
394 elem.get(NULL, "row_id", &row_id);
396 error = enable_rule(row_id);
397 if (error != ERR_NONE) {
398 _E("Re-enable rule%d failed(%d)", row_id, error);
400 _D("Re-enable rule%d succeeded", row_id);
407 bool ctx::rule_manager::rule_data_arr_elem_equals(ctx::json& lelem, ctx::json& relem)
409 std::string lkey, rkey;
410 lelem.get(NULL, CT_RULE_DATA_KEY, &lkey);
411 relem.get(NULL, CT_RULE_DATA_KEY, &rkey);
412 if (lkey.compare(rkey))
415 int lvc, rvc, lvoc, rvoc;
416 lvc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
417 rvc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
418 lvoc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
419 rvoc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
420 if (!((lvc == rvc) && (lvc == lvoc) && (lvc && rvoc)))
424 std::string lop, rop;
425 lelem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &lop);
426 relem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &rop);
427 if (lop.compare(rop))
431 for (int i = 0; i < lvc; i++) {
434 lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &lv);
435 lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, i, &lvo);
437 for (int j = 0; j < lvc; j++) {
439 relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, j, &rv);
440 relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, j, &rvo);
442 if (!lv.compare(rv) && !lvo.compare(rvo)) {
454 bool ctx::rule_manager::rule_item_equals(ctx::json& litem, ctx::json& ritem)
457 std::string lei, rei;
458 litem.get(NULL, CT_RULE_EVENT_ITEM, &lei);
459 ritem.get(NULL, CT_RULE_EVENT_ITEM, &rei);
460 if (lei.compare(rei))
464 ctx::json loption, roption;
465 std::string linst, rinst;
466 litem.get(NULL, CT_RULE_EVENT_OPTION, &loption);
467 ritem.get(NULL, CT_RULE_EVENT_OPTION, &roption);
468 linst = get_instance_name(lei, loption);
469 rinst = get_instance_name(rei, roption);
470 if (linst.compare(rinst))
474 ledac = litem.array_get_size(NULL, CT_RULE_DATA_ARR);
475 redac = ritem.array_get_size(NULL, CT_RULE_DATA_ARR);
479 // Compare item operator;
481 std::string leop, reop;
482 litem.get(NULL, CT_RULE_EVENT_OPERATOR, &leop);
483 ritem.get(NULL, CT_RULE_EVENT_OPERATOR, &reop);
484 if (leop.compare(reop))
488 for (int i = 0; i < ledac; i++) {
491 litem.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &lelem);
493 for (int j = 0; j < ledac; j++) {
495 ritem.get_array_elem(NULL, CT_RULE_DATA_ARR, j, &relem);
497 if (rule_data_arr_elem_equals(lelem, relem)) {
509 bool ctx::rule_manager::rule_equals(ctx::json& lrule, ctx::json& rrule)
513 lrule.get(NULL, CT_RULE_EVENT, &le);
514 rrule.get(NULL, CT_RULE_EVENT, &re);
515 if (!rule_item_equals(le, re))
518 // Compare conditions
520 lcc = lrule.array_get_size(NULL, CT_RULE_CONDITION);
521 rcc = rrule.array_get_size(NULL, CT_RULE_CONDITION);
526 std::string lop, rop;
527 lrule.get(NULL, CT_RULE_OPERATOR, &lop);
528 rrule.get(NULL, CT_RULE_OPERATOR, &rop);
529 if (lop.compare(rop))
533 for (int i = 0; i < lcc; i++) {
536 lrule.get_array_elem(NULL, CT_RULE_CONDITION, i, &lc);
538 for (int j = 0; j < lcc; j++) {
540 rrule.get_array_elem(NULL, CT_RULE_CONDITION, j, &rc);
542 if (rule_item_equals(lc, rc)) {
552 ctx::json laction, raction;
553 lrule.get(NULL, CT_RULE_ACTION, &laction);
554 rrule.get(NULL, CT_RULE_ACTION, &raction);
555 if (laction != raction)
561 int64_t ctx::rule_manager::get_duplicated_rule_id(std::string creator, ctx::json& rule)
563 std::string q = "SELECT row_id, description, details FROM context_trigger_rule WHERE creator = '";
567 std::vector<json> d_record;
568 bool ret = db_manager::execute_sync(q.c_str(), &d_record);
569 IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_id, details by creator failed");
572 rule.get(NULL, CT_RULE_DETAILS, &r_details);
574 rule.get(NULL, CT_RULE_DESCRIPTION, &r_desc);
575 std::vector<json>::iterator vec_end = d_record.end();
577 for (std::vector<json>::iterator vec_pos = d_record.begin(); vec_pos != vec_end; ++vec_pos) {
578 ctx::json elem = *vec_pos;
582 elem.get(NULL, "details", &details);
585 if (rule_equals(r_details, d_details)) {
587 elem.get(NULL, "row_id", &row_id);
589 // Description comparison
591 elem.get(NULL, "description", &d_desc);
592 if (r_desc.compare(d_desc)) {
593 // Only description is changed
594 std::string q_update = "UPDATE context_trigger_rule SET description='" + r_desc + "' WHERE row_id = " + int_to_string(row_id);
596 std::vector<json> record;
597 ret = db_manager::execute_sync(q_update.c_str(), &record);
599 _D("Rule%lld description is updated", row_id);
601 _W("Failed to update description of rule%lld", row_id);
612 int ctx::rule_manager::verify_rule(ctx::json& rule, const char* creator)
615 rule.get(NULL, CT_RULE_DETAILS, &details);
618 rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name);
620 IF_FAIL_RETURN_TAG(c_monitor.is_supported(e_name), ERR_NOT_SUPPORTED, _I, "Event(%s) is not supported", e_name.c_str());
623 if (!c_monitor.is_allowed(creator, e_name.c_str())) {
624 _W("Permission denied for '%s'", e_name.c_str());
625 return ERR_PERMISSION_DENIED;
630 for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){
632 it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name);
634 IF_FAIL_RETURN_TAG(c_monitor.is_supported(c_name), ERR_NOT_SUPPORTED, _I, "Condition(%s) is not supported", c_name.c_str());
636 if (!c_monitor.is_allowed(creator, c_name.c_str())) {
637 _W("Permission denied for '%s'", c_name.c_str());
638 return ERR_PERMISSION_DENIED;
645 int ctx::rule_manager::add_rule(std::string creator, const char* app_id, ctx::json rule, ctx::json* rule_id)
647 // * Insert rule to DB
651 // Check if all items are supported && allowed to access
652 int err = verify_rule(rule, creator.c_str());
653 IF_FAIL_RETURN(err==ERR_NONE, err);
655 // Check if duplicated rule exits
656 if ((rid = get_duplicated_rule_id(creator, rule)) > 0) {
658 rule_id->set(NULL, CT_RULE_ID, rid);
659 _D("Duplicated rule found");
663 // Insert rule to rule table, get rule id and save it to json parameter
665 std::string description;
667 rule.get(NULL, CT_RULE_DESCRIPTION, &description);
668 rule.get(NULL, CT_RULE_DETAILS, &details);
669 r_record.set(NULL, "creator", creator);
671 r_record.set(NULL, "creator_app_id", app_id);
673 r_record.set(NULL, "description", description);
674 r_record.set(NULL, "details", details.str());
675 ret = db_manager::insert_sync(RULE_TABLE, r_record, &rid);
676 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert rule to db failed");
679 rule_id->set(NULL, CT_RULE_ID, rid);
681 // Insert event & conditions of a rule into each table
684 ctx::json e_option_j;
687 rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name);
688 rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &e_option_j);
689 e_inst = get_instance_name(e_name, e_option_j);
691 e_record.set(NULL, "rule_id", rid);
692 e_record.set(NULL, "name", e_name);
693 e_record.set(NULL, "instance_name", e_inst);
694 ret = db_manager::insert(1, EVENT_TABLE, e_record, NULL);
695 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert event to db failed");
698 for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){
703 ctx::json tmp_option;
706 it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name);
707 it.get(NULL, CT_RULE_CONDITION_OPTION, &tmp_option);
708 c_inst = get_instance_name(c_name, tmp_option);
709 c_option.set(NULL, CT_RULE_CONDITION_OPTION, tmp_option);
710 c_option_str = c_option.dup_cstr();
712 c_record.set(NULL, "rule_id", rid);
713 c_record.set(NULL, "name", c_name);
714 c_record.set(NULL, "option", (c_option_str)? c_option_str : "");
715 c_record.set(NULL, "instance_name", c_inst);
717 ret = db_manager::insert(2, CONDITION_TABLE, c_record, NULL);
718 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert conditions to db failed");
723 _D("Add rule%d succeeded", (int)rid);
728 int ctx::rule_manager::remove_rule(int rule_id)
730 // Delete rule from DB
733 std::string query = DELETE_RULE_STATEMENT;
734 query += int_to_string(rule_id);
735 std::vector<json> record;
736 ret = db_manager::execute_sync(query.c_str(), &record);
737 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed");
742 int ctx::rule_manager::enable_rule(int rule_id)
744 if (enb_rule_cnt == 0) {
745 IF_FAIL_RETURN_TAG(initialize_clips(), ERR_OPERATION_FAILED, _E, "Failed to init clips");
756 ctx::json jetemplate;
758 ctx::json inst_names;
760 std::vector<json> rule_record;
761 std::vector<json> etmpl_record;
762 std::vector<json> cond_record;
763 std::vector<json> record;
764 std::vector<json>::iterator vec_end;
766 std::string id_str = int_to_string(rule_id);
768 // Get rule json by rule id;
769 query = QUERY_RULE_BY_RULE_ID;
770 query += int_to_string(rule_id);
771 error = (db_manager::execute_sync(query.c_str(), &rule_record))? ERR_NONE : ERR_OPERATION_FAILED;
772 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query rule by rule id failed");
774 rule_record[0].get(NULL, "details", &tmp);
776 jrule.get(NULL, CT_RULE_EVENT, &jevent);
778 // Get event template by rule id
779 query = QUERY_EVENT_TEMPLATE_BY_RULE_ID;
780 query += int_to_string(rule_id);
782 error = (db_manager::execute_sync(query.c_str(), &etmpl_record))? ERR_NONE : ERR_OPERATION_FAILED;
783 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query event template by rule id failed");
785 jetemplate = etmpl_record[0].str();
786 convert_str_to_json(&jetemplate, NULL, "attributes");
787 convert_str_to_json(&jetemplate, NULL, "options");
789 // Query name, instance name & attributes for conditions of the rule
790 query = QUERY_NAME_INSTANCE_NAME_AND_ATTRIBUTES_BY_RULE_ID_STATEMENT;
792 error = (db_manager::execute_sync(query.c_str(), &cond_record))? ERR_NONE : ERR_OPERATION_FAILED;
793 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query condition's names, instance names, attributes by rule id failed");
795 vec_end = cond_record.end();
796 for (std::vector<json>::iterator vec_pos = cond_record.begin(); vec_pos != vec_end; ++vec_pos) {
797 ctx::json elem = *vec_pos;
801 elem.get(NULL, "name", &cname);
802 elem.get(NULL, "instance_name", &ciname);
803 convert_str_to_json(&elem, NULL, "attributes");
805 // For defrule script generation
806 inst_names.set(NULL, cname.c_str(), ciname);
808 if (cname.compare(ciname) != 0) {
809 if (!clips_h->find_instance(ciname)) {
810 std::string makeinst_script = script_generator::generate_makeinstance(elem);
811 error = (makeinst_script.length() > 0)? ERR_NONE : ERR_OPERATION_FAILED;
812 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Make instance script generation failed");
813 error = clips_h->make_instance(makeinst_script);
814 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Add condition instance([%s]) failed", ciname.c_str());
816 cond_cnt_map[ciname] = 1;
818 cond_cnt_map[ciname]++;
824 jetemplate.get(NULL, "name", &ename);
825 error = c_monitor.subscribe(rule_id, ename, jevent);
826 IF_FAIL_CATCH(error == ERR_NONE);
828 // Generate defrule script and execute it
829 script = script_generator::generate_defrule(id_str, jetemplate, jrule, inst_names);
830 error = clips_h->define_rule(script);
831 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Defrule failed");
833 // Update db to set 'enabled'
834 query = UPDATE_RULE_ENABLED_STATEMENT;
836 error = (db_manager::execute_sync(query.c_str(), &record))? ERR_NONE : ERR_OPERATION_FAILED;
837 IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Update db failed");
840 _D(YELLOW("Enable Rule%d succeeded"), rule_id);
845 if (enb_rule_cnt <= 0) {
853 std::string ctx::rule_manager::get_instance_name(std::string name, ctx::json& option)
855 std::string inst_name = name;
856 std::vector<json> record_tmpl;
858 std::list<std::string> option_keys;
860 // Get template for the option
861 std::string q = "SELECT options FROM context_trigger_template WHERE name = '";
864 db_manager::execute_sync(q.c_str(), &record_tmpl);
866 convert_str_to_json(&record_tmpl[0], NULL, "options");
867 record_tmpl[0].get(NULL, "options", &tmpl_c);
869 tmpl_c.get_keys(&option_keys);
871 for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
872 std::string key = (*it);
876 if (option.get(NULL, key.c_str(), &val_str)) {
877 inst_name += INSTANCE_NAME_DELIMITER;
878 inst_name += val_str;
879 } else if (option.get(NULL, key.c_str(), &val)) {
880 inst_name += INSTANCE_NAME_DELIMITER;
881 inst_name += int_to_string(val);
883 inst_name += INSTANCE_NAME_DELIMITER;
890 int ctx::rule_manager::disable_rule(int rule_id)
895 // For event with options
896 // Get rule json by rule id;
897 std::string q1 = QUERY_RULE_BY_RULE_ID;
898 q1 += int_to_string(rule_id);
899 std::vector<json> rule_record;
900 ret = db_manager::execute_sync(q1.c_str(), &rule_record);
901 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rule by rule id failed");
903 rule_record[0].get(NULL, "details", &r1);
906 rule.get(NULL, CT_RULE_EVENT, &event);
908 event.get(NULL, CT_RULE_EVENT_ITEM, &ename);
911 error = c_monitor.unsubscribe(rule_id, ename, event);
912 IF_FAIL_RETURN(error == ERR_NONE, ERR_OPERATION_FAILED);
914 // Undef rule in clips
915 std::string id_str = int_to_string(rule_id);
916 std::string script = script_generator::generate_undefrule(id_str);
917 error = clips_h->route_string_command(script);
918 IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Undefrule failed");
920 // Update db to set 'disabled'
921 std::string q2 = UPDATE_RULE_DISABLED_STATEMENT;
923 std::vector<json> record;
924 ret = db_manager::execute_sync(q2.c_str(), &record);
925 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed");
927 // Remove condition instances
928 std::string q3 = "SELECT name, instance_name FROM context_trigger_condition WHERE rule_id = ";
930 std::vector<json> name_record;
931 ret = db_manager::execute_sync(q3.c_str(), &name_record);
932 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query condition's name, instance names by rule id failed");
934 std::vector<json>::iterator vec_end = name_record.end();
935 for (std::vector<json>::iterator vec_pos = name_record.begin(); vec_pos != vec_end; ++vec_pos) {
936 ctx::json elem = *vec_pos;
940 elem.get(NULL, "name", &cname);
941 elem.get(NULL, "instance_name", &ciname);
943 if (cname.compare(ciname) != 0) {
944 cond_cnt_map[ciname]--;
946 if (cond_cnt_map[ciname] == 0) {
947 error = clips_h->unmake_instance(ciname);
948 IF_FAIL_RETURN(error == ERR_NONE, error);
950 cond_cnt_map.erase(ciname);
955 if (--enb_rule_cnt <= 0) {
962 void ctx::rule_manager::make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption)
964 std::list<std::string> option_keys;
965 ctemplate.get_keys(&option_keys);
967 for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
968 std::string key = (*it);
970 std::string coption_valstr;
971 if (coption->get(NULL, key.c_str(), &coption_valstr)) {
972 if (coption_valstr.find(EVENT_KEY_PREFIX) == 0) {
973 std::string event_key = coption_valstr.substr(1, coption_valstr.length() - 1);
975 std::string e_valstr;
977 if (edata.get(NULL, event_key.c_str(), &e_valstr)) {
978 coption->set(NULL, key.c_str(), e_valstr);
979 } else if (edata.get(NULL, event_key.c_str(), &e_val)) {
980 coption->set(NULL, key.c_str(), e_val);
987 void ctx::rule_manager::on_event_received(std::string item, ctx::json option, ctx::json data)
989 _D(YELLOW("Event(%s(%s) - %s) is invoked."), item.c_str(), option.str().c_str(), data.str().c_str());
990 // TODO: Check permission of an event(item), if permission denied, return
995 // Generate event fact script
996 std::string q1 = "SELECT attributes, options FROM context_trigger_template WHERE name = '";
999 std::vector<json> etemplate_record;
1000 db_manager::execute_sync(q1.c_str(), &etemplate_record);
1002 ctx::json etemplate = etemplate_record[0];
1003 convert_str_to_json(&etemplate, NULL, "attributes");
1004 convert_str_to_json(&etemplate, NULL, "options");
1006 std::string eventfact_str = script_generator::generate_fact(item, etemplate, option, data);
1008 // Get Conditions template of invoked event (db query)
1009 std::string e_inst = get_instance_name(item, option);
1010 std::string query = QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT;
1013 std::vector<json> conds;
1014 ret = db_manager::execute_sync(query.c_str(), &conds);
1015 IF_FAIL_VOID_TAG(ret, _E, "Query condition templates of invoked event failed");
1017 int cond_num = conds.size();
1018 for (int i = 0; i < cond_num; i++) {
1019 convert_str_to_json(&conds[i], NULL, "options");
1020 convert_str_to_json(&conds[i], NULL, "attributes");
1023 conds[i].get(NULL, "name", &cname);
1026 conds[i].get(NULL, "instance_name", &ciname);
1028 std::string coption_str;
1029 conds[i].get(NULL, "option", &coption_str);
1030 ctx::json coption = NULL;
1031 if (!coption_str.empty()) {
1032 ctx::json coption_tmp = coption_str;
1033 coption_tmp.get(NULL, CT_RULE_CONDITION_OPTION, &coption);
1036 // Check if the condition uses event data key as an option
1037 if (ciname.find(EVENT_KEY_PREFIX) != std::string::npos) {
1038 make_condition_option_based_on_event_data(conds[i], data, &coption); //TODO: conds[i] -> "options"
1041 // TODO: Check permission of a condition(cname), if permission granted, read condition data. (or, condition data should be empty json)
1044 ctx::json condition_data;
1045 err = c_monitor.read(cname, coption, &condition_data);
1046 if (err != ERR_NONE)
1048 _D(YELLOW("Condition(%s(%s) - %s)."), cname.c_str(), coption.str().c_str(), condition_data.str().c_str());
1050 // Generate ModifyInstance script // TODO: conds[i] => "attributes"
1051 std::string modifyinst_script = script_generator::generate_modifyinstance(ciname, conds[i], condition_data);
1053 err = clips_h->route_string_command(modifyinst_script);
1054 IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Modify condition instance failed");
1057 // Add fact and Run environment
1058 err = clips_h->add_fact(eventfact_str);
1059 IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Assert event fact failed");
1061 err = clips_h->run_environment();
1062 IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Run environment failed");
1064 // Retract event fact
1065 std::string retract_command = "(retract *)";
1066 err = clips_h->route_string_command(retract_command);
1067 IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Retract event fact failed");
1069 // Clear uninstalled apps' rules if triggered
1070 if (uninstalled_apps.size() > 0) {
1071 err = clear_rule_of_uninstalled_app();
1072 IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Failed to clear uninstalled apps' rules");
1076 static void trigger_action_app_control(ctx::json& action)
1079 std::string appctl_str;
1080 action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str);
1082 char* str = static_cast<char*>(malloc(appctl_str.length()));
1084 _E("Memory allocation failed");
1087 appctl_str.copy(str, appctl_str.length(), 0);
1088 bundle_raw* encoded = reinterpret_cast<unsigned char*>(str);
1089 bundle* appctl_bundle = bundle_decode(encoded, appctl_str.length());
1091 app_control_h app = NULL;
1092 app_control_create(&app);
1093 app_control_import_from_bundle(app, appctl_bundle);
1095 error = app_control_send_launch_request(app, NULL, NULL);
1096 if (error != APP_CONTROL_ERROR_NONE) {
1097 _E("Launch request failed(%d)", error);
1099 _D("Launch request succeeded");
1101 bundle_free(appctl_bundle);
1103 app_control_destroy(app);
1105 error = device_display_change_state(DISPLAY_STATE_NORMAL);
1106 if (error != DEVICE_ERROR_NONE) {
1107 _E("Change display state failed(%d)", error);
1111 static void trigger_action_notification(ctx::json& action, std::string app_id)
1114 notification_h notification = notification_create(NOTIFICATION_TYPE_NOTI);
1116 if (action.get(NULL, CT_RULE_ACTION_NOTI_TITLE, &title)) {
1117 error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_TITLE, title.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE);
1118 if (error != NOTIFICATION_ERROR_NONE) {
1119 _E("Set notification title failed(%d)", error);
1123 std::string content;
1124 if (action.get(NULL, CT_RULE_ACTION_NOTI_CONTENT, &content)) {
1125 error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_CONTENT, content.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE);
1126 if (error != NOTIFICATION_ERROR_NONE) {
1127 _E("Set notification contents failed(%d)", error);
1131 std::string image_path;
1132 if (action.get(NULL, CT_RULE_ACTION_NOTI_ICON_PATH, &image_path)) {
1133 error = notification_set_image(notification, NOTIFICATION_IMAGE_TYPE_ICON, image_path.c_str());
1134 if (error != NOTIFICATION_ERROR_NONE) {
1135 _E("Set notification icon image failed(%d)", error);
1139 std::string appctl_str;
1141 bundle_raw* encoded = NULL;
1142 bundle* appctl_bundle = NULL;
1143 app_control_h app = NULL;
1144 if (action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str)) {
1145 str = static_cast<char*>(malloc(appctl_str.length()));
1147 _E("Memory allocation failed");
1148 notification_free(notification);
1151 appctl_str.copy(str, appctl_str.length(), 0);
1152 encoded = reinterpret_cast<unsigned char*>(str);
1153 appctl_bundle = bundle_decode(encoded, appctl_str.length());
1155 app_control_create(&app);
1156 app_control_import_from_bundle(app, appctl_bundle);
1158 error = notification_set_launch_option(notification, NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, app);
1159 if (error != NOTIFICATION_ERROR_NONE) {
1160 _E("Set launch option failed(%d)", error);
1164 if (!app_id.empty()) {
1165 error = notification_set_pkgname(notification, app_id.c_str());
1166 if (error != NOTIFICATION_ERROR_NONE) {
1167 _E("Set pkgname(%s) failed(%d)", app_id.c_str(), error);
1172 error = system_settings_get_value_bool(SYSTEM_SETTINGS_KEY_SOUND_SILENT_MODE, &silent);
1173 if (error != SYSTEM_SETTINGS_ERROR_NONE) {
1174 _E("Get system setting(silent mode) failed(%d)", error);
1177 bool vibration = true;
1178 error = runtime_info_get_value_bool(RUNTIME_INFO_KEY_VIBRATION_ENABLED, &vibration);
1179 if (error != RUNTIME_INFO_ERROR_NONE) {
1180 _E("Get runtime info(vibration) failed(%d)", error);
1184 error = notification_set_sound(notification, NOTIFICATION_SOUND_TYPE_DEFAULT, NULL);
1185 if (error != NOTIFICATION_ERROR_NONE) {
1186 _E("Set notification sound failed(%d)", error);
1190 error = notification_set_vibration(notification, NOTIFICATION_VIBRATION_TYPE_DEFAULT, NULL);
1191 if (error != NOTIFICATION_ERROR_NONE) {
1192 _E("Set notification vibration failed(%d)", error);
1197 error = notification_post(notification);
1198 if (error != NOTIFICATION_ERROR_NONE) {
1199 _E("Post notification failed(%d)", error);
1201 _D("Post notification succeeded");
1204 bundle_free(appctl_bundle);
1206 notification_free(notification);
1208 app_control_destroy(app);
1211 error = device_display_change_state(DISPLAY_STATE_NORMAL);
1212 if (error != DEVICE_ERROR_NONE) {
1213 _E("Change display state failed(%d)", error);
1217 static void trigger_action_dbus_call(ctx::json& action)
1219 std::string bus_name, object, iface, method;
1220 GVariant *param = NULL;
1222 action.get(NULL, CT_RULE_ACTION_DBUS_NAME, &bus_name);
1223 IF_FAIL_VOID_TAG(!bus_name.empty(), _E, "No target bus name");
1225 action.get(NULL, CT_RULE_ACTION_DBUS_OBJECT, &object);
1226 IF_FAIL_VOID_TAG(!object.empty(), _E, "No object path");
1228 action.get(NULL, CT_RULE_ACTION_DBUS_INTERFACE, &iface);
1229 IF_FAIL_VOID_TAG(!iface.empty(), _E, "No interface name");
1231 action.get(NULL, CT_RULE_ACTION_DBUS_METHOD, &method);
1232 IF_FAIL_VOID_TAG(!method.empty(), _E, "No method name");
1234 action.get(NULL, CT_RULE_ACTION_DBUS_PARAMETER, ¶m);
1236 ctx::dbus_server::call(bus_name.c_str(), object.c_str(), iface.c_str(), method.c_str(), param);
1239 void ctx::rule_manager::on_rule_triggered(int rule_id)
1241 std::string q = "SELECT details, creator_app_id FROM context_trigger_rule WHERE row_id =";
1242 q += int_to_string(rule_id);
1243 std::vector<json> record;
1244 db_manager::execute_sync(q.c_str(), &record);
1245 if (record.empty()) {
1246 _E("Rule%d not exist", rule_id);
1250 // If rule's creator is uninstalled, skip action
1252 record[0].get(NULL, "creator_app_id", &app_id);
1253 if (is_uninstalled_package(app_id)) {
1254 _D(YELLOW("Rule%d's creator(%s) is uninstalled. Skip action."), rule_id, app_id.c_str());
1255 uninstalled_apps.insert(app_id);
1259 _D(YELLOW("Rule%d is triggered"), rule_id);
1262 std::string details_str;
1263 record[0].get(NULL, "details", &details_str);
1264 ctx::json details = details_str;
1266 details.get(NULL, CT_RULE_ACTION, &action);
1269 if (action.get(NULL, CT_RULE_ACTION_TYPE, &type)) {
1270 if (type.compare(CT_RULE_ACTION_TYPE_APP_CONTROL) == 0) {
1271 trigger_action_app_control(action);
1272 } else if (type.compare(CT_RULE_ACTION_TYPE_NOTIFICATION) == 0) {
1273 trigger_action_notification(action, app_id);
1274 } else if (type.compare(CT_RULE_ACTION_TYPE_DBUS_CALL) == 0) {
1275 trigger_action_dbus_call(action);
1280 int ctx::rule_manager::check_rule(std::string creator, int rule_id)
1282 // Get creator app id
1283 std::string q = "SELECT creator FROM context_trigger_rule WHERE row_id =";
1284 q += int_to_string(rule_id);
1286 std::vector<json> record;
1287 bool ret = db_manager::execute_sync(q.c_str(), &record);
1288 IF_FAIL_RETURN_TAG(ret, false, _E, "Query creator by rule id failed");
1290 if (record.size() == 0) {
1295 record[0].get(NULL, "creator", &c);
1297 if (c.compare(creator) == 0){
1304 bool ctx::rule_manager::is_rule_enabled(int rule_id)
1306 std::string q = "SELECT enabled FROM context_trigger_rule WHERE row_id =";
1307 q += int_to_string(rule_id);
1309 std::vector<json> record;
1310 bool ret = db_manager::execute_sync(q.c_str(), &record);
1311 IF_FAIL_RETURN_TAG(ret, false, _E, "Query enabled by rule id failed");
1314 record[0].get(NULL, "enabled", &enabled);
1322 int ctx::rule_manager::get_rule_by_id(std::string creator, int rule_id, ctx::json* request_result)
1324 std::string q = "SELECT description FROM context_trigger_rule WHERE (creator = '";
1326 q += "') and (row_id = ";
1327 q += int_to_string(rule_id);
1330 std::vector<json> record;
1331 bool ret = db_manager::execute_sync(q.c_str(), &record);
1332 IF_FAIL_RETURN_TAG(ret, false, _E, "Query rule by rule id failed");
1334 if (record.size() == 0) {
1336 } else if (record.size() != 1) {
1337 return ERR_OPERATION_FAILED;
1340 std::string description;
1341 record[0].get(NULL, "description", &description);
1343 (*request_result).set(NULL, CT_RULE_ID, rule_id);
1344 (*request_result).set(NULL, CT_RULE_DESCRIPTION, description);
1349 int ctx::rule_manager::get_rule_ids(std::string creator, ctx::json* request_result)
1351 (*request_result) = "{ \"" CT_RULE_ARRAY_ENABLED "\" : [ ] , \"" CT_RULE_ARRAY_DISABLED "\" : [ ] }";
1353 std::string q = "SELECT row_id, enabled FROM context_trigger_rule WHERE (creator = '";
1357 std::vector<json> record;
1358 bool ret = db_manager::execute_sync(q.c_str(), &record);
1359 IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rules failed");
1361 std::vector<json>::iterator vec_end = record.end();
1362 for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
1363 ctx::json elem = *vec_pos;
1367 elem.get(NULL, "row_id", &id);
1368 elem.get(NULL, "enabled", &enabled);
1371 (*request_result).array_append(NULL, CT_RULE_ARRAY_ENABLED, string_to_int(id));
1372 } else if (enabled == 0) {
1373 (*request_result).array_append(NULL, CT_RULE_ARRAY_DISABLED, string_to_int(id));