Migrate from 2.4 code repo 87/41387/1
authorMu-Woong <muwoong.lee@samsung.com>
Mon, 15 Jun 2015 02:46:19 +0000 (11:46 +0900)
committerMu-Woong <muwoong.lee@samsung.com>
Mon, 15 Jun 2015 02:46:19 +0000 (11:46 +0900)
Change-Id: I3186ef9a0ee6cf56696ec64e6962823023420fda
Signed-off-by: Mu-Woong <muwoong.lee@samsung.com>
51 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
data/access-config.xml [new file with mode: 0644]
data/template-json-to-sql.sh [new file with mode: 0644]
data/trigger-template.json [new file with mode: 0644]
packaging/context-service.manifest [new file with mode: 0644]
packaging/context-service.service [new file with mode: 0644]
packaging/context-service.spec [new file with mode: 0644]
packaging/context-service.xml [new file with mode: 0644]
packaging/org.tizen.context.service [new file with mode: 0644]
src/access_control/config_loader.cpp [new file with mode: 0644]
src/access_control/config_loader.h [new file with mode: 0644]
src/access_control/privilege.cpp [new file with mode: 0644]
src/access_control/privilege.h [new file with mode: 0644]
src/client_request.cpp [new file with mode: 0644]
src/client_request.h [new file with mode: 0644]
src/context_mgr_impl.cpp [new file with mode: 0644]
src/context_mgr_impl.h [new file with mode: 0644]
src/context_trigger/clips_handler.cpp [new file with mode: 0644]
src/context_trigger/clips_handler.h [new file with mode: 0644]
src/context_trigger/context_monitor.cpp [new file with mode: 0644]
src/context_trigger/context_monitor.h [new file with mode: 0644]
src/context_trigger/fact.cpp [new file with mode: 0644]
src/context_trigger/fact.h [new file with mode: 0644]
src/context_trigger/fact_reader.cpp [new file with mode: 0644]
src/context_trigger/fact_reader.h [new file with mode: 0644]
src/context_trigger/fact_request.cpp [new file with mode: 0644]
src/context_trigger/fact_request.h [new file with mode: 0644]
src/context_trigger/rule_manager.cpp [new file with mode: 0644]
src/context_trigger/rule_manager.h [new file with mode: 0644]
src/context_trigger/script_generator.cpp [new file with mode: 0644]
src/context_trigger/script_generator.h [new file with mode: 0644]
src/context_trigger/timer.cpp [new file with mode: 0644]
src/context_trigger/timer.h [new file with mode: 0644]
src/context_trigger/timer_types.h [new file with mode: 0644]
src/context_trigger/trigger.cpp [new file with mode: 0644]
src/context_trigger/trigger.h [new file with mode: 0644]
src/db_mgr_impl.cpp [new file with mode: 0644]
src/db_mgr_impl.h [new file with mode: 0644]
src/dbus_server_impl.cpp [new file with mode: 0644]
src/dbus_server_impl.h [new file with mode: 0644]
src/request.cpp [new file with mode: 0644]
src/request.h [new file with mode: 0644]
src/server.cpp [new file with mode: 0644]
src/server.h [new file with mode: 0644]
src/timer_mgr_impl.cpp [new file with mode: 0644]
src/timer_mgr_impl.h [new file with mode: 0644]
src/zone_util_impl.cpp [new file with mode: 0644]
src/zone_util_impl.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a01ee28
--- /dev/null
@@ -0,0 +1 @@
+.*.swp
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..d97a819
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+Mu-Woong Lee <muwoong.lee@samsung.com>
+Jihoon Park  <jhp27.park@samsung.com>
+Somin Kim    <somin926.kim@samsung.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4233766
--- /dev/null
@@ -0,0 +1,70 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(context-service)
+INCLUDE(GNUInstallDirs)
+
+# Targets
+SET(target "contextd")
+
+# Source Lists
+FILE(GLOB_RECURSE SRCS src/*.cpp)
+MESSAGE("Sources: ${SRCS}")
+
+# Dependencies
+SET(DEPS
+       vconf
+       libxml-2.0
+       sqlite3
+       capi-system-info
+       capi-appfw-app-manager
+       appsvc
+       pkgmgr-info
+       capi-security-privilege-manager
+       vasum
+       alarm-service
+       notification
+       clips
+       context-common
+       device-context-provider
+       statistics-context-provider
+       place-context-provider
+)
+
+# Dependencies regarding profiles
+IF("${PROFILE}" STREQUAL "mobile")
+       ADD_DEFINITIONS("-D_MOBILE")
+ENDIF("${PROFILE}" STREQUAL "mobile")
+
+IF("${PROFILE}" STREQUAL "wearable")
+       ADD_DEFINITIONS("-D_WEARABLE")
+ENDIF("${PROFILE}" STREQUAL "wearable")
+
+# Target vs Emulator
+IF("${ARCH}" STREQUAL "arm")
+       ADD_DEFINITIONS("-D_TARGET")
+ELSE("${ARCH}" STREQUAL "arm")
+       ADD_DEFINITIONS("-D_EMULATOR")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+# Common Options
+INCLUDE(FindPkgConfig)
+INCLUDE_DIRECTORIES(
+       /usr/include
+       /usr/include/glib-2.0
+)
+ADD_DEFINITIONS(-g -O2 -Wall -fPIC -fvisibility=hidden -Wl,--as-needed)
+
+# Building Daemon
+pkg_check_modules(daemon_pkg REQUIRED ${DEPS})
+
+SET(DAEMON_EXTRA_CFLAGS -fPIE)
+FOREACH(flag ${daemon_pkg_CFLAGS})
+       SET(DAEMON_EXTRA_CFLAGS "${DAEMON_EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+ADD_EXECUTABLE(${target} ${SRCS})
+TARGET_LINK_LIBRARIES(${target} ${daemon_pkg_LDFLAGS} -pie)
+SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_FLAGS ${DAEMON_EXTRA_CFLAGS})
+SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_DEFINITIONS "LOG_TAG=\"CONTEXT\"")
+
+# Installing Daemon
+INSTALL(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..1da314d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/data/access-config.xml b/data/access-config.xml
new file mode 100644 (file)
index 0000000..2c4f6c0
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+\r
+<ContextAccessConfig>\r
+\r
+       <Privilege name="alarm.set">\r
+               <Allow subject="timer/event/on_time"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="network.get">\r
+               <Allow subject="system/state/wifi"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="telephony">\r
+               <Allow subject="social/state/call"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="message.read">\r
+               <Allow subject="social/event/message"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="location">\r
+               <Allow subject="place/event/geofence"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="apphistory.read">\r
+               <Allow subject="app/history/recently_used"/>\r
+               <Allow subject="app/history/frequently_used"/>\r
+               <Allow subject="app/history/rarely_used"/>\r
+               <Allow subject="app/history/peak_time"/>\r
+               <Allow subject="app/history/common_setting"/>\r
+               <Allow subject="app/history/use_freq"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="mediahistory.read">\r
+               <Allow subject="music/history/peak_time"/>\r
+               <Allow subject="video/history/peak_time"/>\r
+               <Allow subject="music/history/common_setting"/>\r
+               <Allow subject="video/history/common_setting"/>\r
+               <Allow subject="music/history/play_freq"/>\r
+               <Allow subject="video/history/play_freq"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="mediahistory.admin">\r
+               <Allow subject="music/event/start"/>\r
+               <Allow subject="music/event/stop"/>\r
+               <Allow subject="video/event/start"/>\r
+               <Allow subject="video/event/stop"/>\r
+       </Privilege>\r
+\r
+       <Privilege name="callhistory.read">\r
+               <Allow subject="contact/history/comm_freq"/>\r
+       </Privilege>\r
+\r
+</ContextAccessConfig>\r
diff --git a/data/template-json-to-sql.sh b/data/template-json-to-sql.sh
new file mode 100644 (file)
index 0000000..c5dba2c
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+function print_sql {
+       echo "UPDATE context_trigger_template SET j_template='{$2 }' WHERE name='$1';"
+       echo "INSERT OR IGNORE INTO context_trigger_template (name, j_template) VALUES ('$1', '{$2 }');"
+}
+
+echo "CREATE TABLE IF NOT EXISTS context_trigger_template ("
+echo " name TEXT DEFAULT '' NOT NULL PRIMARY KEY,"
+echo " j_template TEXT DEFAULT '' NOT NULL);"
+
+template_json_file="$1"
+
+while read -r line
+do
+       keyword=`echo $line | awk '{print $1}' | tr -cd '[:alpha:]'`
+
+       if [[ $keyword = "name" ]]; then
+
+               if [ -n "$template" ]; then
+                       print_sql "$name" "$template"
+                       template=""
+               fi
+
+               name=`echo $line | tr -d '":,' | awk '{print $2}'`
+       fi
+
+       if [[ $keyword = "name" ]] || [[ $keyword = "attributes" ]] || [[ $keyword = "option" ]]; then
+               template="$template $line"
+       fi
+
+done < "$template_json_file"
+
+print_sql "$name" "$template"
diff --git a/data/trigger-template.json b/data/trigger-template.json
new file mode 100644 (file)
index 0000000..5a32ec9
--- /dev/null
@@ -0,0 +1,105 @@
+{
+       "templates": [
+               {
+                       "name": "timer/event",
+                       "attributes": ["TimeOfDay", "DayOfWeek"]
+               },
+               {
+                       "name": "timer/state",
+                       "attributes": ["TimeOfDay", "DayOfWeek", "DayOfMonth"]
+               },
+               {
+                       "name": "system/state/battery",
+                       "attributes": ["Level", "IsCharging"]
+               },
+               {
+                       "name": "system/state/charger",
+                       "attributes": ["IsConnected"]
+               },
+               {
+                       "name": "system/state/flight_mode",
+                       "attributes": ["IsEnabled"]
+               },
+               {
+                       "name": "system/state/gps",
+                       "attributes": ["State"]
+               },
+               {
+                       "name": "system/state/headphone",
+                       "attributes": ["IsConnected", "Type"]
+               },
+               {
+                       "name": "system/state/ps_mode",
+                       "attributes": ["IsEnabled"]
+               },
+               {
+                       "name": "system/state/silent_mode",
+                       "attributes": ["IsEnabled"]
+               },
+               {
+                       "name": "system/state/vibration_mode",
+                       "attributes": ["IsEnabled"]
+               },
+               {
+                       "name": "system/state/usb",
+                       "attributes": ["IsConnected"]
+               },
+               {
+                       "name": "system/state/wifi",
+                       "attributes": ["State", "BSSID"]
+               },
+               {
+                       "name": "social/state/call",
+                       "attributes": ["Medium", "State", "Address"]
+               },
+               {
+                       "name": "social/event/email",
+                       "attributes": ["Event"]
+               },
+               {
+                       "name": "social/event/message",
+                       "attributes": ["Event", "Type", "Address"]
+               },
+               {
+                       "name": "activity/event/stationary",
+                       "attributes": ["Event", "Accuracy"]
+               },
+               {
+                       "name": "activity/event/walking",
+                       "attributes": ["Event", "Accuracy"]
+               },
+               {
+                       "name": "activity/event/running",
+                       "attributes": ["Event", "Accuracy"]
+               },
+               {
+                       "name": "activity/event/in_vehicle",
+                       "attributes": ["Event", "Accuracy"]
+               },
+               {
+                       "name": "place/event/geofence",
+                       "attributes": ["Event"],
+                       "option": ["PlaceId"]
+               },
+               {
+                       "name": "app/history/use_freq",
+                       "attributes": ["Rank", "TotalCount"],
+                       "option": ["AppId", "TimeOfDay", "DayOfWeek"]
+               },
+               {
+                       "name": "contact/history/comm_freq",
+                       "attributes": ["Rank", "TotalCount"],
+                       "option": ["Address", "TimeOfDay", "DayOfWeek"]
+               },
+               {
+                       "name": "music/history/play_freq",
+                       "attributes": ["TotalCount"],
+                       "option": ["TimeOfDay", "DayOfWeek"]
+               },
+               {
+                       "name": "video/history/play_freq",
+                       "attributes": ["TotalCount"],
+                       "option": ["TimeOfDay", "DayOfWeek"]
+               }
+       ]
+}
diff --git a/packaging/context-service.manifest b/packaging/context-service.manifest
new file mode 100644 (file)
index 0000000..9ee0705
--- /dev/null
@@ -0,0 +1,68 @@
+<manifest>
+       <define>
+               <domain name="context-service"/>
+               <permit>
+                       <smack permit="dbus" type="rwx"/>
+               </permit>
+               <request>
+                       <smack request="xorg" type="rw"/>
+                       <smack request="security-server::api-cookie-check" type="w"/>
+                       <smack request="security-server::api-privilege-by-pid" type="w"/>
+                       <smack request="vasum::manage" type="rw"/>
+                       <smack request="sys-assert::core" type="rwxat"/>
+                       <smack request="system::vconf" type="rwxl"/>
+                       <smack request="system::vconf_system" type="rl"/>
+                       <smack request="system::vconf_inhouse" type="rl"/>
+                       <smack request="system::vconf_multimedia" type="rl"/>
+                       <smack request="device::app_logging" type="rw"/>
+                       <smack request="device::sys_logging" type="rw"/>
+                       <smack request="deviced::display" type="rw"/>
+                       <smack request="ail::db" type="rlx"/>
+                       <smack request="pkgmgr" type="r"/>
+                       <smack request="pkgmgr::db" type="rwx"/>
+                       <smack request="pkgmgr::info" type="r"/>
+                       <smack request="alarm-server::alarm" type="rw"/>
+                       <smack request="libaccounts-svc" type="r"/>
+                       <smack request="libaccounts-svc::db" type="rl"/>
+                       <smack request="libaccounts-svc::check_read" type="r"/>
+                       <smack request="telephony_framework::api_sim" type="r"/>
+                       <smack request="telephony_framework::api_phonebook" type="rx"/>
+                       <smack request="telephony_framework::properties" type="rw"/>
+                       <smack request="connman::get" type="rw"/>
+                       <smack request="connman::set" type="rw"/>
+                       <smack request="connman::profile" type="rw"/>
+                       <smack request="telephony_framework::api_ps_public" type="rw"/>
+                       <smack request="telephony_framework::api_ps_profile" type="rw"/>
+                       <smack request="telephony_framework::api_sms" type="rwx"/>
+                       <smack request="telephony_framework::api_network" type="rwx"/>
+                       <smack request="telephony_framework::api_call" type="rwx"/>
+                       <smack request="privilege::tizen::telephony.admin" type="rw"/>
+                       <smack request="privilege::tizen::telephony" type="rw"/>
+                       <smack request="data-provider-master::notification" type="rwl"/>
+                       <smack request="data-provider-master::notification.client" type="rw"/>
+                       <smack request="aul::launch" type="x"/>
+                       <smack request="privilege::tizen::call" type="rw"/>
+                       <smack request="privilege::tizen::bluetooth" type="rw"/>
+                       <smack request="privilege::tizen::network.get" type="rw"/>
+                       <smack request="privilege::tizen::network.profile" type="rw"/>
+                       <smack request="privilege::tizen::message.read" type="rw"/>
+                       <smack request="msg-service" type="rwx"/>
+                       <smack request="msg-service::db" type="r"/>
+                       <smack request="msg-service::read" type="rw"/>
+                       <smack request="msg-service::vconf" type="r"/>
+                       <smack request="tizen::vconf::message" type="r"/>
+                       <smack request="privilege::tizen::message.write" type="rw"/>
+                       <smack request="privilege::tizen::message.read" type="rw"/>
+                       <smack request="msg-service::write" type="rw"/>
+                       <smack request="msg-service::read" type="rw"/>
+                       <smack request="app-svc::jump" type="x"/>
+                       <smack request="tizen::vconf::public::r::platform::rw" type="rlw"/>
+               </request>
+       </define>
+       <request>
+               <domain name="context-service"/>
+       </request>
+       <assign>
+               <filesystem path="/usr/bin/contextd" exec_label="context-service" label="context-service"/>
+       </assign>
+</manifest>
diff --git a/packaging/context-service.service b/packaging/context-service.service
new file mode 100644 (file)
index 0000000..d8f8a21
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Contextual Service Framework Daemon
+
+[Service]
+Type=dbus
+User=system
+Group=system
+SmackProcessLabel=context-service
+BusName=org.tizen.context
+ExecStart=/usr/bin/contextd
+Restart=always
+RestartSec=1
+
+[Install]
+WantedBy=graphical.target
diff --git a/packaging/context-service.spec b/packaging/context-service.spec
new file mode 100644 (file)
index 0000000..879c694
--- /dev/null
@@ -0,0 +1,124 @@
+Name:       context-service
+Summary:    Context-Service
+Version:    0.4.4
+Release:    1
+Group:      Framework/system
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+Source1:       context-service.service
+Source2:       org.tizen.context.service
+
+BuildRequires: cmake
+BuildRequires: sed
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(sqlite3)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(capi-appfw-app-manager)
+BuildRequires: pkgconfig(appsvc)
+BuildRequires: pkgconfig(pkgmgr-info)
+BuildRequires: pkgconfig(capi-security-privilege-manager)
+BuildRequires: pkgconfig(vasum)
+BuildRequires: pkgconfig(alarm-service)
+BuildRequires: pkgconfig(notification)
+
+BuildRequires: pkgconfig(clips)
+BuildRequires: pkgconfig(context-common)
+BuildRequires: pkgconfig(context)
+BuildRequires: context-internal
+
+BuildRequires: pkgconfig(device-context-provider)
+BuildRequires: pkgconfig(statistics-context-provider)
+BuildRequires: pkgconfig(place-context-provider)
+
+Requires(preun): /usr/bin/systemctl
+Requires(post): sys-assert
+Requires(post): /usr/bin/systemctl
+Requires(post): /usr/bin/sqlite3
+Requires(postun): /usr/bin/systemctl
+
+%ifarch %{arm}
+%define ARCH arm
+%else
+%define ARCH i586
+%endif
+
+%description
+Context-Service
+
+%prep
+%setup -q
+
+%build
+MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
+
+export   CFLAGS+=" -Wextra -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wswitch-default"
+export CXXFLAGS+=" -Wextra -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wswitch-default -Wnon-virtual-dtor -Wno-c++0x-compat"
+
+export   CFLAGS+=" -Wno-unused-parameter -Wno-empty-body"
+export CXXFLAGS+=" -Wno-unused-parameter -Wno-empty-body"
+
+export   CFLAGS+=" -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-strict-aliasing -fno-unroll-loops -fsigned-char -fstrict-overflow -fno-common"
+export CXXFLAGS+=" -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-strict-aliasing -fno-unroll-loops -fsigned-char -fstrict-overflow"
+#export CXXFLAGS+=" -std=c++0x"
+
+export   CFLAGS+=" -DTIZEN_ENGINEER_MODE"
+export CXXFLAGS+=" -DTIZEN_ENGINEER_MODE"
+export   FFLAGS+=" -DTIZEN_ENGINEER_MODE"
+
+cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DARCH=%{ARCH} -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DPROFILE=%{?tizen_profile_name}
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+mkdir -p %{buildroot}%{_unitdir}
+#mkdir -p %{buildroot}%{_datadir}/dbus-1/services
+mkdir -p %{buildroot}%{_datadir}/license
+mkdir -p %{buildroot}%{_datadir}/packages
+mkdir -p %{buildroot}/opt/dbspace
+mkdir -p %{buildroot}/opt/data/context-service
+sqlite3 %{buildroot}/opt/dbspace/.context-service.db "PRAGMA journal_mode = PERSIST;"
+sqlite3 %{buildroot}/opt/dbspace/.context-service.db "CREATE TABLE VERSION (VERSION TEXT);"
+sqlite3 %{buildroot}/opt/dbspace/.context-service.db "INSERT INTO VERSION VALUES ('%{version}');"
+install -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}
+#install -m 0644 %{SOURCE2} %{buildroot}%{_datadir}/dbus-1/services/
+cp LICENSE %{buildroot}%{_datadir}/license/%{name}
+sed -i "s/^\tversion=\".*\"/\tversion=\"%{version}\"/g" packaging/context-service.xml
+cp packaging/context-service.xml %{buildroot}%{_datadir}/packages/
+cp data/access-config.xml %{buildroot}/opt/data/context-service/
+cp data/trigger-template.json %{buildroot}/opt/data/context-service/
+sh data/template-json-to-sql.sh data/trigger-template.json > %{buildroot}/opt/data/context-service/trigger-template.sql
+
+%post
+sqlite3 -echo /opt/dbspace/.context-service.db < /opt/data/context-service/trigger-template.sql
+mkdir -p %{_unitdir}/graphical.target.wants
+ln -s ../context-service.service %{_unitdir}/graphical.target.wants/
+/sbin/ldconfig
+systemctl daemon-reload
+#if [ $1 == 1 ]; then
+#    systemctl restart context-service
+#fi
+
+%preun
+if [ $1 == 0 ]; then
+    systemctl stop context-service
+fi
+
+%postun
+rm -f %{_unitdir}/graphical.target.wants/context-service.service
+systemctl daemon-reload
+/sbin/ldconfig
+
+%files
+%manifest packaging/%{name}.manifest
+%defattr(-,root,root,-)
+%{_bindir}/*
+%{_unitdir}/context-service.service
+#%{_datadir}/dbus-1/services/org.tizen.context.service
+%{_datadir}/license/%{name}
+%{_datadir}/packages/*.xml
+%defattr(0600,system,system,-)
+/opt/data/context-service/*
+%config(noreplace) /opt/dbspace/.context-service.db*
diff --git a/packaging/context-service.xml b/packaging/context-service.xml
new file mode 100644 (file)
index 0000000..9ed1f7b
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns="http://tizen.org/ns/packages"
+       package="context-service"
+       version="0.0.0"
+       install-location="internal-only"
+       type="rpm">
+       <label>Context Service</label>
+       <description>Context Service</description>
+       <service-application appid="context-service"
+               exec="/usr/bin/contextd"
+               type="capp"
+               multiple="false"
+               taskmanage="false"
+               nodisplay="true"
+               mainapp="true">
+               <label>Context Service</label>
+       </service-application>
+       <privileges>
+               <privilege>http://tizen.org/privilege/location</privilege>
+       </privileges>
+</manifest>
diff --git a/packaging/org.tizen.context.service b/packaging/org.tizen.context.service
new file mode 100644 (file)
index 0000000..702a2df
--- /dev/null
@@ -0,0 +1,6 @@
+[D-BUS Service]
+Name=org.tizen.context
+Exec=/bin/false
+SystemdService=context-service.service
+User=system
+Group=system
diff --git a/src/access_control/config_loader.cpp b/src/access_control/config_loader.cpp
new file mode 100644 (file)
index 0000000..7b56f45
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+
+#include <types_internal.h>
+
+#include "privilege.h"
+#include "config_loader.h"
+
+#define CONFIG_PATH "/opt/data/context-service/access-config.xml"
+#define ELM_ROOT "ContextAccessConfig"
+#define ELM_PRIV "Privilege"
+#define ELM_LINK "Link"
+#define ELM_ALLOW "Allow"
+#define ATTR_NAME "name"
+#define ATTR_SUBJ "subject"
+#define IS_NODE_OF(node, element) (xmlStrcmp((node)->name, (const xmlChar *)(element)) == 0)
+
+static void parse_priv_node(xmlNodePtr cursor)
+{
+       char *prop = (char*)(xmlGetProp(cursor, (const xmlChar*)ATTR_NAME));
+       IF_FAIL_VOID_TAG(prop, _E, "Failed to get '%s'", ATTR_NAME);
+
+       std::string name = prop;
+       free(prop);
+
+       cursor = cursor->xmlChildrenNode;
+       IF_FAIL_VOID_TAG(cursor, _E, "No child node exists");
+
+       while (cursor) {
+               if (!IS_NODE_OF(cursor, ELM_ALLOW)) {
+                       _D("Skipping a node '%s'", (const char*)cursor->name);
+                       cursor = cursor->next;
+                       continue;
+               }
+
+               prop = (char*)(xmlGetProp(cursor, (const xmlChar*)ATTR_SUBJ));
+               if (prop == NULL) {
+                       _E("Failed to get '%s'", ATTR_SUBJ);
+                       cursor = cursor->next;
+                       continue;
+               }
+
+               _SI("Set Privilege: %s <- %s", prop, name.c_str());
+               ctx::privilege_manager::set(prop, name.c_str());
+
+               free(prop);
+               cursor = cursor->next;
+       }
+}
+
+static void parse_link_node(xmlNodePtr cursor)
+{
+       //TODO: Not supported yet
+       _D("Skipping a link node\n");
+}
+
+bool ctx::access_config_loader::load()
+{
+       xmlDocPtr doc = xmlParseFile(CONFIG_PATH);
+       IF_FAIL_RETURN_TAG(doc, false, _E, "XML parsing failed");
+
+       xmlNodePtr cursor = xmlDocGetRootElement(doc);
+       IF_FAIL_CATCH_TAG(cursor, _E, "No root node");
+       IF_FAIL_CATCH_TAG(IS_NODE_OF(cursor, ELM_ROOT), _E, "Invalid root node");
+
+       cursor = cursor->xmlChildrenNode;
+
+       while (cursor) {
+               if (IS_NODE_OF(cursor, ELM_PRIV)) {
+                       parse_priv_node(cursor);
+               } else if (IS_NODE_OF(cursor, ELM_LINK)) {
+                       parse_link_node(cursor);
+               }
+
+               cursor = cursor->next;
+       }
+
+       xmlFreeDoc(doc);
+       return true;
+
+CATCH:
+       xmlFreeDoc(doc);
+       return false;
+}
diff --git a/src/access_control/config_loader.h b/src/access_control/config_loader.h
new file mode 100644 (file)
index 0000000..aa98018
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_ACCESS_CONFIG_LOADER_H__
+#define __CONTEXT_ACCESS_CONFIG_LOADER_H__
+
+namespace ctx {
+       namespace access_config_loader {
+
+               bool load();
+
+       }
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_ACCESS_CONFIG_LOADER_H__ */
diff --git a/src/access_control/privilege.cpp b/src/access_control/privilege.cpp
new file mode 100644 (file)
index 0000000..4af7fe4
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <map>
+
+#include <pkgmgr-info.h>
+#include <privilege_checker.h>
+
+#include <types_internal.h>
+
+#include "config_loader.h"
+#include "privilege.h"
+
+typedef std::map<std::string, std::string> string_map_t;
+
+static string_map_t *privilege_map = NULL;
+
+bool ctx::privilege_manager::init()
+{
+       IF_FAIL_RETURN(privilege_map == NULL, true);
+
+       privilege_map = new(std::nothrow) string_map_t;
+
+       if (!ctx::access_config_loader::load()) {
+               _E("Loading failed");
+               delete privilege_map;
+               return false;
+       }
+
+       return true;
+}
+
+void ctx::privilege_manager::release()
+{
+       delete privilege_map;
+       privilege_map = NULL;
+}
+
+void ctx::privilege_manager::set(const char* subject, const char* priv)
+{
+       (*privilege_map)[subject] = priv;
+}
+
+bool ctx::privilege_manager::is_allowed(const char* pkg_id, const char* subject)
+{
+       IF_FAIL_RETURN_TAG(pkg_id && subject, true, _E, "Invalid parameter");
+       IF_FAIL_RETURN_TAG(pkg_id[0]!='\0' && subject[0]!='\0', true, _E, "Invalid parameter");
+
+       string_map_t::iterator it = privilege_map->find(subject);
+       if (it == privilege_map->end()) {
+               // Non-Privileged Subject
+               return true;
+       }
+
+       _D("PkgId: %s, Priv: %s", pkg_id, (it->second).c_str());
+       std::string priv = "http://tizen.org/privilege/";
+       priv += (it->second).c_str();
+       int ret = privilege_checker_check_package_privilege(pkg_id, priv.c_str());
+       _D("Privilege Check Result: %#x", ret);
+       return (ret == PRIV_CHECKER_ERR_NONE);
+}
+
+std::string ctx::privilege_manager::get_pkg_id(const char* app_id)
+{
+       std::string pkg_id;
+       IF_FAIL_RETURN_TAG(app_id, pkg_id, _E, "Null AppId");
+
+       int ret;
+       pkgmgrinfo_appinfo_h app_info;
+
+       ret = pkgmgrinfo_appinfo_get_appinfo(app_id, &app_info);
+       IF_FAIL_RETURN_TAG(ret == PMINFO_R_OK, pkg_id, _E, "Failed to get app_info");
+
+       char *pkg_name = NULL;
+       ret = pkgmgrinfo_appinfo_get_pkgname(app_info, &pkg_name);
+       if (ret != PMINFO_R_OK || pkg_name == NULL) {
+               pkgmgrinfo_appinfo_destroy_appinfo(app_info);
+               _E("Failed to get package name");
+               return pkg_id;
+       }
+
+       pkg_id = pkg_name;
+       pkgmgrinfo_appinfo_destroy_appinfo(app_info);
+       return pkg_id;
+}
diff --git a/src/access_control/privilege.h b/src/access_control/privilege.h
new file mode 100644 (file)
index 0000000..3744945
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_PRIVILEGE_MANAGER_H__
+#define __CONTEXT_PRIVILEGE_MANAGER_H__
+
+#include <string>
+
+namespace ctx {
+       namespace privilege_manager {
+
+               bool init();
+               void release();
+
+               void set(const char* subject, const char* priv);
+               bool is_allowed(const char* pkg_id, const char* subject);
+               std::string get_pkg_id(const char* app_id);
+
+       }       /* namespace ctx::privilege_manager */
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_PRIVILEGE_MANAGER_H__ */
diff --git a/src/client_request.cpp b/src/client_request.cpp
new file mode 100644 (file)
index 0000000..d1b8a95
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <glib.h>
+#include <security-server.h>
+#include <app_manager.h>
+#include <types_internal.h>
+#include <dbus_server.h>
+#include "zone_util_impl.h"
+#include "dbus_server_impl.h"
+#include "access_control/privilege.h"
+#include "client_request.h"
+
+ctx::client_request::client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv)
+       : request_info(type, client, req_id, subj, desc)
+       , invocation(inv)
+{
+       gsize size;
+       int client_pid;
+       char *decoded = NULL;
+       const char *zone_name = NULL;
+       char *pkg_id = NULL;
+
+       decoded = reinterpret_cast<char*>(g_base64_decode(cookie, &size));
+       IF_FAIL_CATCH_TAG(decoded, _E, "Cookie decoding failed");
+
+       raw_cookie = decoded;
+       client_pid = security_server_get_cookie_pid(decoded);
+       pkg_id = security_server_get_smacklabel_cookie(decoded);
+       g_free(decoded);
+       IF_FAIL_CATCH_TAG(client_pid > 0, _E, "Invalid PID (%d)", client_pid);
+
+       if (pkg_id == NULL) {
+               _W(RED("security_server_get_smacklabel_cookie() failed"));
+               char* app_id = NULL;
+               app_manager_get_app_id(client_pid, &app_id);
+               client_app_id = ctx::privilege_manager::get_pkg_id(app_id);
+               g_free(app_id);
+       } else {
+               //FIXME: Yes.. this is actually the package id
+               client_app_id = pkg_id;
+               g_free(pkg_id);
+       }
+
+       zone_name = ctx::zone_util::get_name_by_pid(client_pid);
+       IF_FAIL_CATCH_TAG(zone_name, _E, RED("Zone name retrieval failed"));
+       _zone_name = zone_name;
+
+       _SD(CYAN("Package: '%s' / Zone: '%s'"), client_app_id.c_str(), zone_name);
+       return;
+
+CATCH:
+       invocation = NULL;
+       throw ERR_OPERATION_FAILED;
+}
+
+ctx::client_request::~client_request()
+{
+       if (invocation)
+               g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
+}
+
+const char* ctx::client_request::get_cookie()
+{
+       return raw_cookie.c_str();
+}
+
+const char* ctx::client_request::get_app_id()
+{
+       if (!client_app_id.empty())
+               return client_app_id.c_str();
+
+       return NULL;
+}
+
+bool ctx::client_request::reply(int error)
+{
+       IF_FAIL_RETURN(invocation, true);
+
+       _I("Reply %#x", error);
+
+       g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
+       invocation = NULL;
+       return true;
+}
+
+bool ctx::client_request::reply(int error, ctx::json& request_result)
+{
+       IF_FAIL_RETURN(invocation, true);
+       IF_FAIL_RETURN(_type != REQ_READ_SYNC, true);
+
+       char *result = request_result.dup_cstr();
+       IF_FAIL_RETURN_TAG(result, false, _E, "Memory allocation failed");
+
+       _I("Reply %#x", error);
+       _SD("Result: %s", result);
+
+       g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, EMPTY_JSON_OBJECT));
+       invocation = NULL;
+
+       g_free(result);
+       return true;
+}
+
+bool ctx::client_request::reply(int error, ctx::json& request_result, ctx::json& data_read)
+{
+       if (invocation == NULL) {
+               return publish(error, data_read);
+       }
+
+       char *result = NULL;
+       char *data = NULL;
+
+       result = request_result.dup_cstr();
+       IF_FAIL_CATCH_TAG(result, _E, "Memory allocation failed");
+
+       data = data_read.dup_cstr();
+       IF_FAIL_CATCH_TAG(data, _E, "Memory allocation failed");
+
+       _I("Reply %#x", error);
+       _SD("Result: %s", result);
+       _SD("Data: %s", data);
+
+       g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, data));
+       invocation = NULL;
+
+       g_free(result);
+       g_free(data);
+       return true;
+
+CATCH:
+       g_free(result);
+       g_free(data);
+       return false;
+}
+
+bool ctx::client_request::publish(int error, ctx::json& data)
+{
+       char *data_str = data.dup_cstr();
+       IF_FAIL_RETURN_TAG(data_str, false, _E, "Memory allocation failed");
+
+       dbus_server::publish(_client.c_str(), _req_id, _subject.c_str(), error, data_str);
+       g_free(data_str);
+
+       return true;
+}
diff --git a/src/client_request.h b/src/client_request.h
new file mode 100644 (file)
index 0000000..fc4737e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_CLIENT_REQUEST_H__
+#define __CONTEXT_CLIENT_REQUEST_H__
+
+#include <gio/gio.h>
+#include "request.h"
+
+namespace ctx {
+
+       class client_request : public request_info {
+               public:
+                       client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv);
+                       ~client_request();
+
+                       const char* get_cookie();
+                       const char* get_app_id();
+
+                       bool reply(int error);
+                       bool reply(int error, ctx::json& request_result);
+                       bool reply(int error, ctx::json& request_result, ctx::json& data_read);
+                       bool publish(int error, ctx::json& data);
+
+               private:
+                       GDBusMethodInvocation *invocation;
+                       std::string raw_cookie;
+                       std::string client_app_id;
+                       std::string exec_path;
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_CLIENT_REQUEST_H__ */
diff --git a/src/context_mgr_impl.cpp b/src/context_mgr_impl.cpp
new file mode 100644 (file)
index 0000000..159cdc5
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <string>
+
+#include <types_internal.h>
+#include <json.h>
+#include <provider_iface.h>
+#include "server.h"
+#include "context_mgr_impl.h"
+#include "zone_util_impl.h"
+#include "access_control/privilege.h"
+
+/* Analyzer Headers */
+#include <internal/device_context_provider.h>
+#include <internal/app_statistics_provider.h>
+#include <internal/social_statistics_provider.h>
+#include <internal/media_statistics_provider.h>
+#include <internal/place_context_provider.h>
+
+ctx::context_manager_impl::context_manager_impl()
+{
+}
+
+ctx::context_manager_impl::~context_manager_impl()
+{
+       release();
+}
+
+bool ctx::context_manager_impl::init()
+{
+       IF_FAIL_RETURN_TAG(provider_list.size()==0, false, _W, "Re-initialization");
+
+       try {
+               /* List of all providers */
+               load_provider(new ctx::device_context_provider());
+               load_provider(new ctx::app_statistics_provider());
+               load_provider(new ctx::social_statistics_provider());
+               load_provider(new ctx::media_statistics_provider());
+               load_provider(new ctx::place_context_provider());
+
+       } catch (std::bad_alloc& ba) {
+               _E("Analyzer loading failed (bad alloc)");
+               return false;
+       } catch (int e) {
+               _E("Analyzer loading failed (%#x)", e);
+               return false;
+       }
+
+       return true;
+}
+
+void ctx::context_manager_impl::release()
+{
+       subject_provider_map.clear();
+
+       for (provider_list_t::iterator it = provider_list.begin(); it != provider_list.end(); ++it) {
+               delete *it;
+       }
+       provider_list.clear();
+
+       for (request_list_t::iterator it = subscribe_request_list.begin(); it != subscribe_request_list.end(); ++it) {
+               delete *it;
+       }
+       subscribe_request_list.clear();
+
+       for (request_list_t::iterator it = read_request_list.begin(); it != read_request_list.end(); ++it) {
+               delete *it;
+       }
+       read_request_list.clear();
+}
+
+void ctx::context_manager_impl::load_provider(context_provider_iface* provider)
+{
+       if (!provider) {
+               _E("Analyzer NULL");
+               throw static_cast<int>(ERR_INVALID_PARAMETER);
+       }
+
+       provider_list.push_back(provider);
+
+       if (!provider->init()) {
+               _E("Analyzer initialization failed");
+               throw ERR_OPERATION_FAILED;
+       }
+}
+
+bool ctx::context_manager_impl::register_provider(const char* subject, ctx::context_provider_iface* cp)
+{
+       IF_FAIL_RETURN_TAG(subject && cp, false, _E, "Invalid parameter");
+
+       if (subject_provider_map.find(subject) != subject_provider_map.end()) {
+               _E("The provider for the subject '%s' is already registered.", subject);
+               return false;
+       }
+
+       _I("Registering provider for '%s'", subject);
+       subject_provider_map[subject] = cp;
+
+       return true;
+}
+
+void ctx::context_manager_impl::assign_request(ctx::request_info* request)
+{
+       int req_type = request->get_type();
+       context_provider_iface *provider = NULL;
+
+       if (req_type != REQ_UNSUBSCRIBE) {
+               subject_provider_map_t::iterator it = subject_provider_map.find(request->get_subject());
+               if (it == subject_provider_map.end()) {
+                       _E("Unknown subject '%s'", request->get_subject());
+                       request->reply(ERR_NOT_SUPPORTED);
+                       delete request;
+                       return;
+               }
+               provider = it->second;
+       }
+
+       std::string app_id;
+       // If the ClientAppId attribute exists but is empty.
+       if (request->get_description().get(NULL, COMMON_ATTR_CLIENT_APP_ID, &app_id)) {
+               if (app_id.empty() && request->get_app_id()) {
+                       request->get_description().set(NULL, COMMON_ATTR_CLIENT_APP_ID, request->get_app_id());
+               }
+       }
+
+       switch (req_type) {
+               case REQ_SUBSCRIBE:
+                       if (!check_permission(request)) {
+                               request->reply(ERR_PERMISSION_DENIED);
+                               delete request;
+                               break;
+                       }
+                       subscribe(request, provider);
+                       break;
+               case REQ_UNSUBSCRIBE:
+                       unsubscribe(request);
+                       break;
+               case REQ_READ:
+               case REQ_READ_SYNC:
+                       if (!check_permission(request)) {
+                               request->reply(ERR_PERMISSION_DENIED);
+                               delete request;
+                               break;
+                       }
+                       read(request, provider);
+                       break;
+               case REQ_WRITE:
+                       if (!check_permission(request)) {
+                               request->reply(ERR_PERMISSION_DENIED);
+                               delete request;
+                               break;
+                       }
+                       write(request, provider);
+                       break;
+               case REQ_SUPPORT:
+                       if (provider->is_supported(request->get_subject(), request->get_zone_name())) {
+                               request->reply(ERR_NONE);
+                       } else {
+                               request->reply(ERR_NOT_SUPPORTED);
+                       }
+                       delete request;
+                       break;
+               default:
+                       _E("Invalid type of request");
+                       delete request;
+       }
+}
+
+bool ctx::context_manager_impl::is_supported(const char* subject, const char* zone)
+{
+       subject_provider_map_t::iterator it = subject_provider_map.find(subject);
+       IF_FAIL_RETURN(it != subject_provider_map.end(), false);
+
+       return it->second->is_supported(subject, zone);
+}
+
+ctx::context_manager_impl::request_list_t::iterator
+ctx::context_manager_impl::find_request(request_list_t& r_list, std::string subject, json& option, const char* zone)
+{
+       return find_request(r_list.begin(), r_list.end(), subject, option, zone);
+}
+
+ctx::context_manager_impl::request_list_t::iterator
+ctx::context_manager_impl::find_request(request_list_t& r_list, std::string client, int req_id)
+{
+       request_list_t::iterator it;
+       for (it = r_list.begin(); it != r_list.end(); ++it) {
+               if (client == (*it)->get_client() && req_id == (*it)->get_id()) {
+                       break;
+               }
+       }
+       return it;
+}
+
+ctx::context_manager_impl::request_list_t::iterator
+ctx::context_manager_impl::find_request(request_list_t::iterator begin, request_list_t::iterator end, std::string subject, json& option, const char* zone)
+{
+       //TODO: Do we need to consider the case that the inparam option is a subset of the request description?
+       request_list_t::iterator it;
+       for (it = begin; it != end; ++it) {
+               if (subject == (*it)->get_subject() && option == (*it)->get_description() && STR_EQ(zone, (*it)->get_zone_name())) {
+                       break;
+               }
+       }
+       return it;
+}
+
+bool ctx::context_manager_impl::check_permission(ctx::request_info* request)
+{
+       const char* app_id = request->get_app_id();
+       _D("Peer AppID: %s", app_id);
+       IF_FAIL_RETURN_TAG(app_id, false, _E, "AppID NULL");
+       IF_FAIL_RETURN_TAG(!STR_EQ(app_id, TRIGGER_CLIENT_NAME), true, _D, "Skipping permission check for Trigger");
+
+       scope_zone_joiner sz(request->get_zone_name());
+
+       bool allowed = ctx::privilege_manager::is_allowed(app_id, request->get_subject());
+       IF_FAIL_RETURN_TAG(allowed, false, _W, "Permission denied");
+
+       return true;
+}
+
+void ctx::context_manager_impl::subscribe(ctx::request_info* request, ctx::context_provider_iface* provider)
+{
+       _I(CYAN("[%s] '%s' subscribes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
+
+       ctx::json request_result;
+       int error = provider->subscribe(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name());
+
+       _D("Analyzer returned %d", error);
+
+       if (!request->reply(error, request_result) || error != ERR_NONE) {
+               delete request;
+               return;
+       }
+
+       subscribe_request_list.push_back(request);
+}
+
+void ctx::context_manager_impl::unsubscribe(ctx::request_info* request)
+{
+       _I(CYAN("[%s] '%s' unsubscribes RID-%d"), request->get_zone_name(), request->get_client(), request->get_id());
+
+       // Search the subscribe request to be removed
+       request_list_t::iterator target = find_request(subscribe_request_list, request->get_client(), request->get_id());
+       if (target == subscribe_request_list.end()) {
+               _W("Unknown request");
+               delete request;
+               return;
+       }
+
+       // Keep the pointer to the request found
+       request_info *req_found = *target;
+
+       // Remove the request from the list
+       subscribe_request_list.erase(target);
+
+       // Check if there exist the same requests
+       if (find_request(subscribe_request_list, req_found->get_subject(), req_found->get_description(), req_found->get_zone_name()) != subscribe_request_list.end()) {
+               // Do not stop detecting the subject
+               _D("A same request from '%s' exists", req_found->get_client());
+               request->reply(ERR_NONE);
+               delete request;
+               delete req_found;
+               return;
+       }
+
+       // Find the proper provider
+       subject_provider_map_t::iterator ca = subject_provider_map.find(req_found->get_subject());
+       if (ca == subject_provider_map.end()) {
+               _E("Invalid subject '%s'", req_found->get_subject());
+               delete request;
+               delete req_found;
+               return;
+       }
+
+       // Stop detecting the subject
+       int error = ca->second->unsubscribe(req_found->get_subject(), req_found->get_description(), req_found->get_zone_name());
+       request->reply(error);
+       delete request;
+       delete req_found;
+}
+
+void ctx::context_manager_impl::read(ctx::request_info* request, ctx::context_provider_iface* provider)
+{
+       _I(CYAN("[%s] '%s' reads '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
+
+       ctx::json request_result;
+       int error = provider->read(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name());
+
+       _D("Analyzer returned %d", error);
+
+       if (!request->reply(error, request_result) || error != ERR_NONE) {
+               delete request;
+               return;
+       }
+
+       read_request_list.push_back(request);
+}
+
+void ctx::context_manager_impl::write(ctx::request_info* request, ctx::context_provider_iface* provider)
+{
+       _I(CYAN("[%s] '%s' writes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id());
+
+       ctx::json request_result;
+       int error = provider->write(request->get_subject(), request->get_description(), &request_result, request->get_zone_name());
+
+       _D("Analyzer returned %d", error);
+
+       request->reply(error, request_result);
+       delete request;
+}
+
+bool ctx::context_manager_impl::_publish(const char* subject, ctx::json option, int error, ctx::json data_updated, const char* zone)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+
+       _I("Publishing '%s'", subject);
+       _J("Option", option);
+
+       request_list_t::iterator end = subscribe_request_list.end();
+       request_list_t::iterator target = find_request(subscribe_request_list.begin(), end, subject, option, zone);
+
+       while (target != end) {
+               if (!(*target)->publish(error, data_updated)) {
+                       return false;
+               }
+               target = find_request(++target, end, subject, option, zone);
+       }
+
+       return true;
+}
+
+bool ctx::context_manager_impl::_reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read, const char* zone)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+
+       _I("Sending data of '%s'", subject);
+       _J("Option", option);
+       _J("Data", data_read);
+       _SI("Zone: '%s'", zone);
+
+       request_list_t::iterator end = read_request_list.end();
+       request_list_t::iterator target = find_request(read_request_list.begin(), end, subject, option, zone);
+       request_list_t::iterator prev;
+
+       ctx::json dummy;
+
+       while (target != end) {
+               (*target)->reply(error, dummy, data_read);
+               prev = target;
+               target = find_request(++target, end, subject, option, zone);
+
+               delete *prev;
+               read_request_list.erase(prev);
+       }
+
+       return true;
+}
+
+struct published_data_s {
+       int type;
+       ctx::context_manager_impl *mgr;
+       std::string subject;
+       std::string zone;
+       int error;
+       ctx::json option;
+       ctx::json data;
+       published_data_s(int t, ctx::context_manager_impl *m, const char* s, ctx::json& o, int e, ctx::json& d, const char* z)
+               : type(t), mgr(m), subject(s), error(e)
+       {
+               option = o.str();
+               data = d.str();
+               zone = z;
+       }
+};
+
+gboolean ctx::context_manager_impl::thread_switcher(gpointer data)
+{
+       published_data_s *tuple = static_cast<published_data_s*>(data);
+
+       switch (tuple->type) {
+               case REQ_SUBSCRIBE:
+                       tuple->mgr->_publish(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str());
+                       break;
+               case REQ_READ:
+                       tuple->mgr->_reply_to_read(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str());
+                       break;
+               default:
+                       _W("Invalid type");
+       }
+
+       delete tuple;
+       return FALSE;
+}
+
+bool ctx::context_manager_impl::publish(const char* subject, ctx::json& option, int error, ctx::json& data_updated, const char* zone)
+{
+       IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter");
+
+       published_data_s *tuple = new(std::nothrow) published_data_s(REQ_SUBSCRIBE, this, subject, option, error, data_updated, zone);
+       IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed");
+
+       g_idle_add(thread_switcher, tuple);
+
+       return true;
+}
+
+bool ctx::context_manager_impl::reply_to_read(const char* subject, ctx::json& option, int error, ctx::json& data_read, const char* zone)
+{
+       IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter");
+
+       published_data_s *tuple = new(std::nothrow) published_data_s(REQ_READ, this, subject, option, error, data_read, zone);
+       IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed");
+
+       g_idle_add(thread_switcher, tuple);
+
+       return true;
+}
diff --git a/src/context_mgr_impl.h b/src/context_mgr_impl.h
new file mode 100644 (file)
index 0000000..9598d04
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_MANAGER_IMPL_H__
+#define __CONTEXT_MANAGER_IMPL_H__
+
+#include <vector>
+#include <list>
+#include <map>
+#include <context_mgr_iface.h>
+#include "request.h"
+
+#define TRIGGER_CLIENT_NAME "TRIGGER"
+
+namespace ctx {
+
+       class context_manager_impl : public context_manager_iface {
+               public:
+                       typedef std::list<request_info*> request_list_t;
+
+                       context_manager_impl();
+                       ~context_manager_impl();
+
+                       bool init();
+                       void release();
+
+                       void assign_request(ctx::request_info* request);
+                       bool is_supported(const char* subject, const char* zone);
+
+                       // From the interface class
+                       bool register_provider(const char* subject, ctx::context_provider_iface* cp);
+                       bool publish(const char* subject, ctx::json& option, int error, ctx::json& data_updated, const char* zone);
+                       bool reply_to_read(const char* subject, ctx::json& option, int error, ctx::json& data_read, const char* zone);
+                       // ---
+
+               private:
+                       typedef std::vector<context_provider_iface*> provider_list_t;
+                       typedef std::map<std::string, context_provider_iface*> subject_provider_map_t;
+
+                       provider_list_t provider_list;
+                       request_list_t subscribe_request_list;
+                       request_list_t read_request_list;
+                       subject_provider_map_t subject_provider_map;
+
+                       void load_provider(ctx::context_provider_iface* provider);
+
+                       void subscribe(request_info* request, context_provider_iface* provider);
+                       void unsubscribe(request_info* request);
+                       void read(request_info* request, context_provider_iface* provider);
+                       void write(request_info* request, context_provider_iface* provider);
+
+                       bool check_permission(request_info* request);
+
+                       static gboolean thread_switcher(gpointer data);
+                       bool _publish(const char* subject, ctx::json option, int error, ctx::json data_updated, const char* zone);
+                       bool _reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read, const char* zone);
+
+                       request_list_t::iterator find_request(request_list_t& r_list, std::string subject, json& option, const char* zone);
+                       request_list_t::iterator find_request(request_list_t& r_list, std::string client, int req_id);
+                       request_list_t::iterator find_request(request_list_t::iterator begin, request_list_t::iterator end, std::string subject, json& option, const char* zone);
+
+       };      /* class context_manager_impl */
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_MANAGER_IMPL_H__ */
diff --git a/src/context_trigger/clips_handler.cpp b/src/context_trigger/clips_handler.cpp
new file mode 100644 (file)
index 0000000..4ac8378
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * context-service
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <types_internal.h>
+#include <json.h>
+#include <string>
+#include <sstream>
+#include <cstdlib>
+#include "clips_handler.h"
+#include "rule_manager.h"
+
+extern "C"
+{
+#include <clips/clips.h>
+}
+
+static void* env = NULL;
+static ctx::rule_manager* rule_mgr = NULL;
+static int string_to_int(std::string str);
+
+ctx::clips_handler::clips_handler()
+{
+}
+
+ctx::clips_handler::~clips_handler()
+{
+       if (env) {
+               DestroyEnvironment(env);
+       }
+}
+
+bool ctx::clips_handler::init(ctx::rule_manager* rm)
+{
+       rule_mgr = rm;
+
+       int error = init_environment(env);
+       IF_FAIL_RETURN(error == ERR_NONE, false);
+
+       bool ret = define_global_variable_string("zone", "");
+       IF_FAIL_RETURN(ret, false);
+
+       return true;
+}
+
+int ctx::clips_handler::init_environment(void* &environment)
+{
+       environment = CreateEnvironment();
+       if (!environment) {
+               _E("Create environment failed");
+               return ERR_OPERATION_FAILED;
+       }
+
+       char* func_name = strdup("execute_action");
+       char* restrictions = strdup("1s");
+
+       if (func_name == NULL || restrictions == NULL) {
+               _E("Memory allocation failed");
+               free(func_name);
+               free(restrictions);
+               return ERR_OUT_OF_MEMORY;
+       }
+
+       EnvDefineFunction2(environment, func_name, 'i', PTIEF execute_action, func_name, restrictions);
+
+       free(func_name);
+       free(restrictions);
+
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::define_template(std::string& script)
+{
+       IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Deftemplate failed");
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::define_class(std::string& script)
+{
+       IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defclass failed");
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::define_rule(std::string& script)
+{
+       IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defrule failed");
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::env_build(std::string& script)
+{
+       ASSERT_NOT_NULL(env);
+       if (script.length() == 0)
+               return ERR_INVALID_PARAMETER;
+
+       _I("EnvBuild script (%s)", script.c_str());
+       int ret = EnvBuild(env, script.c_str());
+
+       return (ret == 1)? ERR_NONE : ERR_OPERATION_FAILED;
+}
+
+int ctx::clips_handler::run_environment()
+{
+       ASSERT_NOT_NULL(env);
+
+       int fired_rule_num = EnvRun(env, -1);
+       IF_FAIL_RETURN(fired_rule_num >= 0, ERR_OPERATION_FAILED);
+
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::add_fact(std::string& fact)
+{
+       ASSERT_NOT_NULL(env);
+       if (fact.length() == 0)
+               return ERR_INVALID_PARAMETER;
+
+       void* assert_fact = EnvAssertString(env, fact.c_str());
+       IF_FAIL_RETURN_TAG(assert_fact, ERR_OPERATION_FAILED, _E, "Fact assertion failed");
+
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::route_string_command(std::string& script)
+{
+       ASSERT_NOT_NULL(env);
+       if (script.length() == 0)
+               return ERR_INVALID_PARAMETER;
+
+       int error;
+       if (RouteCommand(env, script.c_str(), TRUE)){
+               _D("Route command succeeded(%s).", script.c_str());
+               error = ERR_NONE;
+       } else {
+               _E("Route command failed");
+               error = ERR_OPERATION_FAILED;
+       }
+
+       return error;
+}
+
+int ctx::clips_handler::make_instance(std::string& script)
+{
+       ASSERT_NOT_NULL(env);
+       if (script.length() == 0)
+               return ERR_INVALID_PARAMETER;
+
+       int error;
+       if (EnvMakeInstance(env, script.c_str())){
+               _D("Make instance succeeded - %s", script.c_str());
+               error = ERR_NONE;
+       } else {
+               _E("Make instance failed");
+               error = ERR_OPERATION_FAILED;
+       }
+
+       return error;
+}
+
+int ctx::clips_handler::unmake_instance(std::string& instance_name)
+{
+       ASSERT_NOT_NULL(env);
+       if (instance_name.length() == 0)
+               return ERR_INVALID_PARAMETER;
+
+       void* instance = find_instance_internal(instance_name);
+       if (!instance) {
+               _E("Cannot find instance(%s).", instance_name.c_str());
+               return ERR_INVALID_PARAMETER;
+       }
+
+       if (!EnvUnmakeInstance(env, instance)){
+               _E("Unmake instance failed");
+               return ERR_OPERATION_FAILED;
+       }
+
+       _D("Unmake instance succeeded(%s).", instance_name.c_str());
+       return ERR_NONE;
+}
+
+int ctx::clips_handler::execute_action()
+{
+       if (!env) {
+               _E("Environment not created");
+               return ERR_OPERATION_FAILED;
+       }
+
+       const char* result = EnvRtnLexeme(env, 1);
+       if (!result) {
+               _E("Failed to get returned rule id");
+               return ERR_OPERATION_FAILED;
+       }
+       std::string rule_id = result;
+       std::string id_str = rule_id.substr(4);
+
+       int id = string_to_int(id_str);
+       rule_mgr->on_rule_triggered(id);
+
+       return ERR_NONE;
+}
+
+bool ctx::clips_handler::find_instance(std::string& instance_name)
+{
+       ASSERT_NOT_NULL(env);
+       if (find_instance_internal(instance_name)) {
+               _D("[%s] already exists", instance_name.c_str());
+               return true;
+       }
+
+       return false;
+}
+
+void* ctx::clips_handler::find_instance_internal(std::string& instance_name)
+{
+       void* instance = EnvFindInstance(env, NULL, instance_name.c_str(), TRUE);
+       return instance;
+}
+
+int string_to_int(std::string str)
+{
+       int i;
+       std::istringstream convert(str);
+
+       if (!(convert >> i))
+               i = 0;
+
+       return i;
+}
+
+bool ctx::clips_handler::define_global_variable_string(std::string variable_name, std::string value)
+{
+       std::string script = "(defglobal ?*";
+       script += variable_name;
+       script += "* = \"";
+       script += value;
+       script += "\")";
+
+       IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, false, _E, "Defglobal failed");
+       return true;
+}
+
+bool ctx::clips_handler::set_global_variable_string(std::string variable_name, std::string value)
+{
+       ASSERT_NOT_NULL(env);
+       if (variable_name.length() == 0)
+               return false;
+
+       DATA_OBJECT data;
+       SetType(data, STRING);
+       SetValue(data, EnvAddSymbol(env, value.c_str())) ;
+
+       int ret = EnvSetDefglobalValue(env, variable_name.c_str(), &data);
+
+       IF_FAIL_RETURN_TAG(ret == 1, false, _E, "Set global variable(%s) failed", variable_name.c_str());
+       return true;
+}
diff --git a/src/context_trigger/clips_handler.h b/src/context_trigger/clips_handler.h
new file mode 100644 (file)
index 0000000..a258b13
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+* context-service
+*
+* Copyright (c) 2014 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+#ifndef __CLIPS_HANDLER_H__
+#define __CLIPS_HANDLER_H__
+
+namespace ctx {
+
+       class json;
+       class rule_manager;
+
+       class clips_handler {
+               public:
+                       clips_handler();
+                       ~clips_handler();
+                       bool init(ctx::rule_manager* rm);
+
+                       int define_template(std::string& script);
+                       int define_class(std::string& script);
+                       int define_rule(std::string& script);
+                       int run_environment();
+                       int add_fact(std::string& fact);
+                       int route_string_command(std::string& fact);
+                       int make_instance(std::string& script);
+                       int unmake_instance(std::string& instance_name);
+                       static int execute_action();
+                       bool find_instance(std::string& instance_name);
+                       bool define_global_variable_string(std::string variable_name, std::string value);
+                       bool set_global_variable_string(std::string variable_name, std::string value);
+
+               private:
+                       int init_environment(void* &environment);
+                       int env_build(std::string& script);
+                       void* find_instance_internal(std::string& instance_name);
+
+   };  /* class clips_handler */
+
+}      /* namespace ctx */
+
+#endif /* End of __CLIPS_HANDLER_H__ */
diff --git a/src/context_trigger/context_monitor.cpp b/src/context_trigger/context_monitor.cpp
new file mode 100644 (file)
index 0000000..8cfb589
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * context-service
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <types_internal.h>
+#include <json.h>
+#include <context_trigger_types_internal.h>
+#include "context_monitor.h"
+#include "fact_reader.h"
+#include "timer_types.h"
+
+static ctx::fact_reader *reader = NULL;
+typedef std::map<std::string, ctx::trigger_timer> timer_map_t;
+static timer_map_t timer_map;  // <zone_name, timer>
+
+ctx::context_monitor::context_monitor()
+{
+}
+
+ctx::context_monitor::~context_monitor()
+{
+}
+
+bool ctx::context_monitor::init(ctx::fact_reader* fr, ctx::context_trigger* tr)
+{
+       reader = fr;
+       trigger = tr;
+
+       return true;
+}
+
+int ctx::context_monitor::subscribe(int rule_id, std::string subject, ctx::json event, const char* zone)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0) {
+               // option is event json in case of ON_TIME
+               return subscribe_timer(event, zone);
+       }
+
+       ctx::json eoption = NULL;
+       event.get(NULL, CT_RULE_EVENT_OPTION, &eoption);
+
+       int req_id = reader->subscribe(subject.c_str(), &eoption, zone, true);
+       IF_FAIL_RETURN_TAG(req_id > 0, ERR_OPERATION_FAILED, _E, "Subscribe event failed");
+       _D(YELLOW("Subscribe event(rule%d). req%d"), rule_id, req_id);
+       request_map[rule_id] = req_id;
+       read_req_cnt_map[req_id]++;
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::unsubscribe(int rule_id, std::string subject, ctx::json option, const char* zone)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0) {
+               return unsubscribe_timer(option, zone);
+       }
+
+       _D(YELLOW("Unsubscribe event(rule%d). req%d"), rule_id, request_map[rule_id]);
+       int req_id = request_map[rule_id];
+       request_map.erase(rule_id);
+
+       read_req_cnt_map[req_id]--;
+       if (read_req_cnt_map[req_id] == 0) {
+               reader->unsubscribe(req_id);
+               read_req_cnt_map.erase(req_id);
+       }
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::read_time(ctx::json* result, const char* zone)
+{
+       int dom = ctx::trigger_timer::get_day_of_month();
+       (*result).set(NULL, TIMER_RESPONSE_KEY_DAY_OF_MONTH, dom);
+
+       std::string dow = ctx::trigger_timer::get_day_of_week();
+       (*result).set(NULL, TIMER_RESPONSE_KEY_DAY_OF_WEEK, dow);
+
+       int time = ctx::trigger_timer::get_minute_of_day();
+       (*result).set(NULL, TIMER_RESPONSE_KEY_TIME_OF_DAY, time);
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::read(std::string subject, json option, const char* zone, ctx::json* result)
+{
+       bool ret;
+       if (subject.compare(TIMER_CONDITION_SUBJECT) == 0) {
+               return read_time(result, zone);
+       }
+
+       context_fact fact;
+       ret = reader->read(subject.c_str(), &option, zone, fact);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Read fact failed");
+
+       *result = fact.get_data();
+
+       return ERR_NONE;
+}
+
+static int arrange_day_of_week(ctx::json day_info)
+{
+       int result = 0;
+
+       std::string key_op;
+       if (!day_info.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &key_op)) {
+               result = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\"");
+               return result;
+       }
+
+       if (key_op.compare("and") == 0) {
+               result = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\"");
+       }
+
+       std::string tmp_d;
+       for (int i = 0; day_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &tmp_d); i++) {
+               int dow = ctx::trigger_timer::convert_string_to_day_of_week(tmp_d);
+               std::string op;
+               day_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, i, &op);
+
+               if (op.compare("neq") == 0) {
+                       dow = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\"") & ~dow;
+               }
+
+               if (key_op.compare("and") == 0) {
+                       result &= dow;
+               } else {
+                       result |= dow;
+               }
+       }
+       _D("Requested day of week (%#x)", result);
+
+       return result;
+}
+
+timer_map_t::iterator ctx::context_monitor::get_zone_timer(std::string zone)
+{
+       timer_map_t::iterator it = timer_map.find(zone);
+
+       if (it == timer_map.end()) {
+               timer_map.insert(std::pair<std::string, ctx::trigger_timer>(zone, ctx::trigger_timer(trigger, zone)));
+       }
+
+       return timer_map.find(zone);
+}
+
+int ctx::context_monitor::subscribe_timer(ctx::json option, const char* zone)
+{
+       ctx::json day_info;
+       ctx::json time_info;
+
+       ctx::json it;
+       for (int i = 0; option.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &it); i++){
+               std::string key;
+               it.get(NULL, CT_RULE_DATA_KEY, &key);
+
+               if (key.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) {
+                       day_info = it;
+               } else if (key.compare(TIMER_RESPONSE_KEY_TIME_OF_DAY) == 0) {
+                       time_info = it;
+               }
+       }
+
+       // Day option processing
+       int dow = arrange_day_of_week(day_info);
+
+       // Time option processing
+       int time; // minute
+       timer_map_t::iterator timer = get_zone_timer(zone);
+       for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) {
+               (timer->second).add(time, dow);
+       }
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::unsubscribe_timer(ctx::json option, const char* zone)
+{
+       ctx::json day_info;
+       ctx::json time_info;
+
+       ctx::json it;
+       for (int i = 0; option.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &it); i++){
+               std::string key;
+               it.get(NULL, CT_RULE_DATA_KEY, &key);
+
+               if (key.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) {
+                       day_info = it;
+               } else if (key.compare(TIMER_RESPONSE_KEY_TIME_OF_DAY) == 0) {
+                       time_info = it;
+               }
+       }
+
+       // Day option processing
+       int dow = arrange_day_of_week(day_info);
+
+       // Time option processing
+       int time; // minute
+       timer_map_t::iterator timer = get_zone_timer(zone);
+       for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) {
+               (timer->second).remove(time, dow);
+       }
+
+       if ((timer->second).empty()) {
+               timer_map.erase(timer);
+       }
+
+       return ERR_NONE;
+}
+
+bool ctx::context_monitor::is_supported(std::string subject, const char* zone)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0
+                       || subject.compare(TIMER_CONDITION_SUBJECT) == 0) {
+               return true;
+       }
+
+       return reader->is_supported(subject.c_str(), zone);
+}
diff --git a/src/context_trigger/context_monitor.h b/src/context_trigger/context_monitor.h
new file mode 100644 (file)
index 0000000..be1695d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* context-service
+*
+* Copyright (c) 2014 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+#ifndef __CONTEXT_MONITOR_H__
+#define __CONTEXT_MONITOR_H__
+
+#include <map>
+#include "timer.h"
+
+namespace ctx {
+
+       class json;
+       class fact_reader;
+       class context_fact;
+
+       class context_monitor {
+          public:
+                       context_monitor();
+                       ~context_monitor();
+                       bool init(ctx::fact_reader* fr, ctx::context_trigger* tr);
+
+                       int subscribe(int rule_id, std::string subject, ctx::json event, const char* zone);
+                       int unsubscribe(int rule_id, std::string subject, ctx::json option, const char* zone);
+                       int read(std::string subject, json option, const char* zone, ctx::json* result);
+                       bool is_supported(std::string subject, const char* zone);
+
+               private:
+                       std::map<std::string, ctx::trigger_timer>::iterator get_zone_timer(std::string zone);
+                       int subscribe_timer(ctx::json option, const char* zone);
+                       int unsubscribe_timer(ctx::json option, const char* zone);
+                       int read_day_of_month(ctx::json* result, const char* zone);
+                       int read_day_of_week(ctx::json* result, const char* zone);
+                       int read_time(ctx::json* result, const char* zone);
+                       std::map<int, int> request_map; // <rule_id, fact_read_req_id>
+                       std::map<int, int> read_req_cnt_map;    // <fact_read_req_id, count>
+                       ctx::context_trigger* trigger;
+
+   };  /* class context_monitor */
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_MONITOR_H__ */
diff --git a/src/context_trigger/fact.cpp b/src/context_trigger/fact.cpp
new file mode 100644 (file)
index 0000000..af83b92
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <types_internal.h>
+#include "fact.h"
+
+ctx::context_fact::context_fact()
+       : req_id(-1)
+       , error(ERR_NONE)
+{
+}
+
+ctx::context_fact::context_fact(int id, int err, const char* s, ctx::json& o, ctx::json& d, const char* z)
+       : req_id(id)
+       , error(err)
+       , subject(s)
+       , option(o)
+       , data(d)
+{
+       if (z) {
+               zone_name = z;
+       }
+}
+
+ctx::context_fact::~context_fact()
+{
+}
+
+void ctx::context_fact::set_req_id(int id)
+{
+       req_id = id;
+}
+
+void ctx::context_fact::set_error(int err)
+{
+       error = err;
+}
+
+void ctx::context_fact::set_subject(const char* s)
+{
+       subject = s;
+}
+
+void ctx::context_fact::set_option(ctx::json& o)
+{
+       option = o;
+}
+
+void ctx::context_fact::set_data(ctx::json& d)
+{
+       data = d;
+}
+
+void ctx::context_fact::set_zone_name(const char* z)
+{
+       zone_name = z;
+}
+
+int ctx::context_fact::get_req_id()
+{
+       return req_id;
+}
+
+int ctx::context_fact::get_error()
+{
+       return error;
+}
+
+const char* ctx::context_fact::get_subject()
+{
+       return subject.c_str();
+}
+
+const char* ctx::context_fact::get_zone_name()
+{
+       return zone_name.c_str();
+}
+
+ctx::json& ctx::context_fact::get_option()
+{
+       return option;
+}
+
+ctx::json& ctx::context_fact::get_data()
+{
+       return data;
+}
diff --git a/src/context_trigger/fact.h b/src/context_trigger/fact.h
new file mode 100644 (file)
index 0000000..f961f96
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_CONTEXT_TRIGGER_FACT_H__
+#define __CONTEXT_CONTEXT_TRIGGER_FACT_H__
+
+#include <string>
+#include <json.h>
+
+namespace ctx {
+
+       class context_fact {
+               private:
+                       int req_id;
+                       int error;
+                       std::string subject;
+                       ctx::json option;
+                       ctx::json data;
+                       std::string zone_name;
+
+               public:
+                       context_fact();
+                       context_fact(int id, int err, const char* s, ctx::json& o, ctx::json& d, const char* z);
+                       ~context_fact();
+
+                       void set_req_id(int id);
+                       void set_error(int err);
+                       void set_subject(const char* s);
+                       void set_option(ctx::json& o);
+                       void set_zone_name(const char* z);
+                       void set_data(ctx::json& d);
+
+                       int get_req_id();
+                       int get_error();
+                       const char* get_subject();
+                       const char* get_zone_name();
+                       ctx::json& get_option();
+                       ctx::json& get_data();
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_CONTEXT_TRIGGER_FACT_H__ */
diff --git a/src/context_trigger/fact_reader.cpp b/src/context_trigger/fact_reader.cpp
new file mode 100644 (file)
index 0000000..fec066f
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <scope_mutex.h>
+#include "trigger.h"
+#include "fact_request.h"
+#include "fact_reader.h"
+
+#define CLIENT_NAME                    TRIGGER_CLIENT_NAME
+#define COND_END_TIME(T)       (g_get_monotonic_time() + (T) * G_TIME_SPAN_SECOND)
+#define SUBSCRIBE_TIMEOUT      3
+#define READ_TIMEOUT           10
+
+static GMutex request_mutex;
+static GCond request_cond;
+static int last_rid;
+static int last_err;
+static ctx::json last_data_read;
+
+ctx::context_manager_impl *ctx::fact_reader::_context_mgr = NULL;
+ctx::context_trigger *ctx::fact_reader::_trigger = NULL;
+
+static int generate_req_id()
+{
+       static int req_id = 0;
+
+       if (++req_id < 0) {
+               // Overflow handling
+               req_id = 1;
+       }
+
+       return req_id;
+}
+
+ctx::fact_reader::fact_reader(context_manager_impl* mgr, context_trigger* trigger)
+{
+       _context_mgr = mgr;
+       _trigger = trigger;
+}
+
+ctx::fact_reader::~fact_reader()
+{
+       for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) {
+               delete *it;
+       }
+       subscr_list.clear();
+}
+
+int ctx::fact_reader::find_sub(const char* subject, json* option, const char* zone)
+{
+       json opt_j;
+       if (option) {
+               opt_j = *option;
+       }
+
+       for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) {
+               if ((*it)->subject == subject && (*it)->option == opt_j && (*it)->zone_name == zone) {
+                       return (*it)->sid;
+               }
+       }
+
+       return -1;
+}
+
+bool ctx::fact_reader::add_sub(int sid, const char* subject, json* option, const char* zone)
+{
+       subscr_info_s *info = new(std::nothrow) subscr_info_s(sid, subject, option, zone);
+       IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed");
+
+       subscr_list.push_back(info);
+       return true;
+}
+
+void ctx::fact_reader::remove_sub(const char* subject, json* option, const char* zone)
+{
+       json opt_j;
+       if (option) {
+               opt_j = *option;
+       }
+
+       for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) {
+               if ((*it)->subject == subject && (*it)->option == opt_j && (*it)->zone_name == zone) {
+                       delete *it;
+                       subscr_list.erase(it);
+                       return;
+               }
+       }
+}
+
+void ctx::fact_reader::remove_sub(int sid)
+{
+       for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) {
+               if ((*it)->sid == sid) {
+                       delete *it;
+                       subscr_list.erase(it);
+                       return;
+               }
+       }
+}
+
+gboolean ctx::fact_reader::send_request(gpointer data)
+{
+       fact_request *req = static_cast<fact_request*>(data);
+       _context_mgr->assign_request(req);
+       return FALSE;
+}
+
+bool ctx::fact_reader::is_supported(const char* subject, const char* zone)
+{
+       IF_FAIL_RETURN_TAG(zone, false, _E, "'zone' cannot be NULL");
+       return _context_mgr->is_supported(subject, zone);
+}
+
+int ctx::fact_reader::subscribe(const char* subject, json* option, const char* zone, bool wait_response)
+{
+       IF_FAIL_RETURN_TAG(zone, ERR_INVALID_PARAMETER, _E, "'zone' cannot be NULL");
+       IF_FAIL_RETURN(subject, ERR_INVALID_PARAMETER);
+
+       ctx::scope_mutex sm(&request_mutex);
+
+       int rid = find_sub(subject, option, zone);
+       if (rid > 0) {
+               _D("Duplicated request for %s", subject);
+               return rid;
+       }
+
+       rid = generate_req_id();
+
+       fact_request *req = new(std::nothrow) fact_request(REQ_SUBSCRIBE, CLIENT_NAME,
+                       rid, subject, option ? option->str().c_str() : NULL, zone, wait_response ? this : NULL);
+       IF_FAIL_RETURN_TAG(req, -1, _E, "Memory allocation failed");
+
+       g_idle_add(send_request, req);
+       add_sub(rid, subject, option, zone);
+
+       IF_FAIL_RETURN_TAG(wait_response, rid, _D, "Ignoring response for %s", subject);
+
+       while (last_rid != rid) {
+               if (!g_cond_wait_until(&request_cond, &request_mutex, COND_END_TIME(SUBSCRIBE_TIMEOUT))) {
+                       _E("Timeout: subject %s", subject);
+                       //TODO: what happens if the request actually takes more time than the timeout
+                       remove_sub(rid);
+                       return -1;
+               }
+       }
+
+       if (last_err != ERR_NONE) {
+               remove_sub(rid);
+               _E("Subscription request failed: %#x", last_err);
+               return -1;
+       }
+
+       return rid;
+}
+
+void ctx::fact_reader::unsubscribe(const char* subject, json* option, const char* zone)
+{
+       IF_FAIL_VOID_TAG(zone, _E, "'zone' cannot be NULL");
+       IF_FAIL_VOID(subject);
+
+       ctx::scope_mutex sm(&request_mutex);
+
+       int rid = find_sub(subject, option, zone);
+       IF_FAIL_VOID_TAG(rid > 0, _W, "Unknown subscription for %s", subject);
+
+       unsubscribe(rid);
+}
+
+void ctx::fact_reader::unsubscribe(int subscription_id)
+{
+       fact_request *req = new(std::nothrow) fact_request(REQ_UNSUBSCRIBE, CLIENT_NAME, subscription_id, "", NULL, NULL, NULL);
+       IF_FAIL_VOID_TAG(req, _E, "Memory allocation failed");
+
+       g_idle_add(send_request, req);
+       remove_sub(subscription_id);
+}
+
+bool ctx::fact_reader::read(const char* subject, json* option, const char* zone, context_fact& fact)
+{
+       IF_FAIL_RETURN_TAG(zone, false, _E, "'zone' cannot be NULL");
+       IF_FAIL_RETURN(subject, false);
+
+       ctx::scope_mutex sm(&request_mutex);
+
+       int rid = generate_req_id();
+
+       fact_request *req = new(std::nothrow) fact_request(REQ_READ_SYNC, CLIENT_NAME,
+                       rid, subject, option ? option->str().c_str() : NULL, zone, this);
+       IF_FAIL_RETURN_TAG(req, false, _E, "Memory allocation failed");
+
+       g_idle_add(send_request, req);
+
+       while (last_rid != rid) {
+               if (!g_cond_wait_until(&request_cond, &request_mutex, COND_END_TIME(READ_TIMEOUT))) {
+                       _E("Timeout: subject %s", subject);
+                       //TODO: what happens if the request actually takes more time than the timeout
+                       return false;
+               }
+       }
+
+       if (last_err != ERR_NONE) {
+               _E("Read request failed: %#x", last_err);
+               return false;
+       }
+
+       fact.set_req_id(rid);
+       fact.set_subject(subject);
+       fact.set_data(last_data_read);
+       if (zone) {
+               fact.set_zone_name(zone);
+       }
+
+       last_data_read = EMPTY_JSON_OBJECT;
+
+       return true;
+}
+
+void ctx::fact_reader::reply_result(int req_id, int error, const char* zone, json* request_result, json* fact)
+{
+       ctx::scope_mutex sm(&request_mutex);
+
+       last_rid = req_id;
+       last_err = error;
+       last_data_read = (fact ? *fact : EMPTY_JSON_OBJECT);
+
+       g_cond_signal(&request_cond);
+}
+
+void ctx::fact_reader::publish_fact(int req_id, int error, const char* zone, const char* subject, json* option, json* fact)
+{
+       _trigger->push_fact(req_id, error, subject, *option, *fact, zone);
+}
diff --git a/src/context_trigger/fact_reader.h b/src/context_trigger/fact_reader.h
new file mode 100644 (file)
index 0000000..13f0742
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_FACT_READER_H__
+#define __CONTEXT_FACT_READER_H__
+
+#include <list>
+#include <json.h>
+#include "../context_mgr_impl.h"
+#include "fact.h"
+
+namespace ctx {
+
+       class context_trigger;
+
+       class fact_reader {
+               public:
+                       fact_reader(context_manager_impl* mgr, context_trigger* trigger);
+                       ~fact_reader();
+
+                       bool is_supported(const char* subject, const char* zone);
+
+                       int subscribe(const char* subject, json* option, const char* zone, bool wait_response = false);
+                       void unsubscribe(const char* subject, json* option, const char* zone);
+                       void unsubscribe(int subscription_id);
+                       bool read(const char* subject, json* option, const char* zone, context_fact& fact);
+
+                       void reply_result(int req_id, int error, const char* zone, json* request_result = NULL, json* fact = NULL);
+                       void publish_fact(int req_id, int error, const char* zone, const char* subject, json* option, json* fact);
+
+               private:
+                       static context_manager_impl* _context_mgr;
+                       static context_trigger* _trigger;
+
+                       struct subscr_info_s {
+                               int sid;
+                               std::string subject;
+                               ctx::json option;
+                               std::string zone_name;
+                               subscr_info_s(int id, const char* subj, ctx::json* opt, const char* zone)
+                                       : sid(id), subject(subj)
+                               {
+                                       if (opt)
+                                               option = *opt;
+
+                                       if (zone)
+                                               zone_name = zone;
+                               }
+                       };
+
+                       typedef std::list<subscr_info_s*> subscr_list_t;
+                       subscr_list_t subscr_list;
+
+                       int find_sub(const char* subject, json* option, const char* zone);
+                       bool add_sub(int sid, const char* subject, json* option, const char* zone);
+                       void remove_sub(const char* subject, json* option, const char* zone);
+                       void remove_sub(int sid);
+
+                       static gboolean send_request(gpointer data);
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_FACT_READER_H__ */
diff --git a/src/context_trigger/fact_request.cpp b/src/context_trigger/fact_request.cpp
new file mode 100644 (file)
index 0000000..0ae9231
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <types_internal.h>
+#include "fact_request.h"
+
+ctx::fact_request::fact_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* zone, fact_reader* reader)
+       : request_info(type, client, req_id, subj, desc)
+       , _reader(reader)
+       , replied(false)
+{
+       if (zone)
+               _zone_name = zone;
+}
+
+ctx::fact_request::~fact_request()
+{
+       reply(ERR_OPERATION_FAILED);
+}
+
+bool ctx::fact_request::reply(int error)
+{
+       IF_FAIL_RETURN(!replied && _reader, true);
+       _reader->reply_result(_req_id, error, _zone_name.c_str());
+       return (replied = true);
+}
+
+bool ctx::fact_request::reply(int error, ctx::json& request_result)
+{
+       IF_FAIL_RETURN(!replied && _reader, true);
+       IF_FAIL_RETURN(_type != REQ_READ_SYNC, true);
+       _reader->reply_result(_req_id, error, _zone_name.c_str(), &request_result);
+       return (replied = true);
+}
+
+bool ctx::fact_request::reply(int error, ctx::json& request_result, ctx::json& data_read)
+{
+       IF_FAIL_RETURN(!replied && _reader, true);
+       _reader->reply_result(_req_id, error, _zone_name.c_str(), &request_result, &data_read);
+       return (replied = true);
+}
+
+bool ctx::fact_request::publish(int error, ctx::json& data)
+{
+       IF_FAIL_RETURN(_reader, true);
+       _reader->publish_fact(_req_id, error, _zone_name.c_str(), _subject.c_str(), &get_description(), &data);
+       return true;
+}
+
+void ctx::fact_request::set_zone_name(const char* zone_name)
+{
+       if (zone_name)
+               _zone_name = zone_name;
+}
diff --git a/src/context_trigger/fact_request.h b/src/context_trigger/fact_request.h
new file mode 100644 (file)
index 0000000..d8d80ba
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_TRIGGER_FACT_REQUEST_H__
+#define __CONTEXT_TRIGGER_FACT_REQUEST_H__
+
+#include "fact_reader.h"
+#include "../request.h"
+
+namespace ctx {
+
+       class fact_request : public request_info {
+               public:
+                       fact_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* zone, fact_reader* reader);
+                       ~fact_request();
+
+                       void set_zone_name(const char* zone_name);
+                       bool reply(int error);
+                       bool reply(int error, ctx::json& request_result);
+                       bool reply(int error, ctx::json& request_result, ctx::json& data_read);
+                       bool publish(int error, ctx::json& data);
+
+               private:
+                       fact_reader *_reader;
+                       bool replied;
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_TRIGGER_FACT_REQUEST_H__ */
diff --git a/src/context_trigger/rule_manager.cpp b/src/context_trigger/rule_manager.cpp
new file mode 100644 (file)
index 0000000..f9c0d71
--- /dev/null
@@ -0,0 +1,1052 @@
+/*
+ * context-service
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <sstream>
+#include <app.h>
+#include <glib.h>
+#include <types_internal.h>
+#include <json.h>
+#include <stdlib.h>
+#include <bundle.h>
+#include <app_control.h>
+#include <appsvc.h>
+#include <app_control_internal.h>
+#include <notification.h>
+#include <notification_internal.h>
+#include <context_trigger_types_internal.h>
+#include <context_trigger.h>
+#include <db_mgr.h>
+#include <zone_util.h>
+#include "../access_control/privilege.h"
+#include "rule_manager.h"
+#include "script_generator.h"
+#include "trigger.h"
+
+#define RULE_TABLE "context_trigger_rule"
+#define EVENT_TABLE "context_trigger_event"
+#define CONDITION_TABLE "context_trigger_condition"
+#define TEMPLATE_TABLE "context_trigger_template"
+
+#define RULE_TABLE_COLUMNS "enabled INTEGER DEFAULT 0 NOT NULL, creator TEXT DEFAULT '' NOT NULL, description TEXT DEFAULT '', details TEXT DEFAULT '' NOT NULL, zone TEXT DEFAULT '' NOT NULL"
+#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 '', zone TEXT DEFAULT '' NOT NULL"
+#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 '', zone TEXT DEFAULT '' NOT NULL"
+#define CREATE_TEMPLATE_TABLE "CREATE TABLE IF NOT EXISTS context_trigger_template (name TEXT DEFAULT '' NOT NULL PRIMARY KEY, j_template TEXT DEFAULT '' NOT NULL)"
+#define QUERY_TEMPLATE_TABLE "SELECT j_template FROM context_trigger_template"
+#define FOREIGN_KEYS_ON "PRAGMA foreign_keys = ON"
+#define DELETE_RULE_STATEMENT "DELETE FROM 'context_trigger_rule' where row_id = "
+#define UPDATE_RULE_ENABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 1 WHERE row_id = "
+#define UPDATE_RULE_DISABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 0 WHERE row_id = "
+#define QUERY_NAME_INSTANCE_NAME_AND_TEMPLATE_BY_RULE_ID_STATEMENT "SELECT context_trigger_condition.name, instance_name, j_template as templates FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id = "
+#define QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT "SELECT DISTINCT context_trigger_condition.name, instance_name, option, j_template 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 = '"
+#define QUERY_RULE_AND_ZONE_BY_RULE_ID "SELECT details, zone FROM context_trigger_rule WHERE row_id = "
+#define QUERY_EVENT_TEMPLATE_BY_RULE_ID "SELECT j_template FROM context_trigger_template WHERE name IN (SELECT name FROM context_trigger_event WHERE rule_id = "
+#define QUERY_CONDITION_BY_RULE_ID "SELECT name, option FROM context_trigger_condition WHERE rule_id = "
+
+#define INSTANCE_NAME_DELIMITER "/"
+#define EVENT_KEY_PREFIX "?"
+
+static ctx::context_trigger* trigger = NULL;
+
+static int string_to_int(std::string str)
+{
+       int i;
+       std::istringstream convert(str);
+
+       if (!(convert >> i))
+               i = 0;
+
+       return i;
+}
+
+static std::string int_to_string(int i)
+{
+       std::ostringstream convert;
+       convert << i;
+       std::string str = convert.str();
+       return str;
+}
+
+ctx::rule_manager::rule_manager()
+{
+}
+
+ctx::rule_manager::~rule_manager()
+{
+}
+
+bool ctx::rule_manager::init(ctx::context_trigger* tr, ctx::fact_reader* fr)
+{
+       int error;
+       bool ret;
+
+       trigger = tr;
+
+       ret = clips_h.init(this);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "CLIPS handler initialization failed");
+
+       ret = c_monitor.init(fr, tr);
+
+       // Create tables into db (rule, event, condition, action, template)
+       ret = db_manager::create_table(1, RULE_TABLE, RULE_TABLE_COLUMNS, NULL, NULL);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Create rule table failed");
+
+       ret = db_manager::create_table(2, EVENT_TABLE, EVENT_TABLE_COLUMNS, NULL, NULL);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Create event table failed");
+
+       ret = db_manager::create_table(3, CONDITION_TABLE, CONDITION_TABLE_COLUMNS, NULL, NULL);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Create condition table failed");
+
+       ret = db_manager::execute(4, CREATE_TEMPLATE_TABLE, NULL);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Create template table failed");
+
+       // Load all templates from DB
+       std::vector<json> record;
+       ret = db_manager::execute_sync(QUERY_TEMPLATE_TABLE, &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query template table failed");
+
+       // Make scripts for deftemplate, defclass, make-instance and load them to clips
+       std::vector<json>::iterator vec_end = record.end();
+       for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+               std::string tmpl_str;
+               elem.get(NULL, "j_template", &tmpl_str);
+               ctx::json tmpl = tmpl_str;
+
+               std::string deftemplate_str = script_generator::generate_deftemplate(&tmpl);
+               error = clips_h.define_template(deftemplate_str);
+               IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Deftemplate failed");
+
+               std::string defclass_str = script_generator::generate_defclass(&tmpl);
+               error = clips_h.define_class(defclass_str);
+               IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Defclass failed");
+
+               std::string makeinstance_str = script_generator::generate_makeinstance(&tmpl);
+               error = clips_h.make_instance(makeinstance_str);
+               IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Makeinstance failed");
+       }
+
+       // Foreign keys on
+       ret = db_manager::execute_sync(FOREIGN_KEYS_ON, &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Foreign keys on failed");
+
+       ret = reenable_rule();
+
+       return ret;
+}
+
+bool ctx::rule_manager::reenable_rule(void)
+{
+       int error;
+       std::string q = "SELECT row_id FROM context_trigger_rule where enabled = 1";
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_ids of enabled rules failed");
+
+       std::vector<json>::iterator vec_end = record.end();
+       for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+               int row_id;
+               elem.get(NULL, "row_id", &row_id);
+
+               error = enable_rule(row_id);
+               if (error != ERR_NONE) {
+                       _E("Re-enable rule%d failed(%d)", row_id, error);
+               } else {
+                       _E("Re-enable rule%d succeeded", row_id);
+               }
+       }
+
+       return true;
+}
+
+bool ctx::rule_manager::rule_data_arr_elem_equals(ctx::json& lelem, ctx::json& relem)
+{
+       std::string lkey, rkey;
+       lelem.get(NULL, CT_RULE_DATA_KEY, &lkey);
+       relem.get(NULL, CT_RULE_DATA_KEY, &rkey);
+       if (lkey.compare(rkey))
+               return false;
+
+       int lvc, rvc, lvoc, rvoc;
+       lvc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
+       rvc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
+       lvoc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
+       rvoc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
+       if (!((lvc == rvc) && (lvc == lvoc) && (lvc && rvoc)))
+               return false;
+
+       if (lvc > 1) {
+               std::string lop, rop;
+               lelem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &lop);
+               relem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &rop);
+               if (lop.compare(rop))
+                       return false;
+       }
+
+       for (int i = 0; i < lvc; i++) {
+               bool found = false;
+               std::string lv, lvo;
+               lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &lv);
+               lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, i, &lvo);
+
+               for (int j = 0; j < lvc; j++) {
+                       std::string rv, rvo;
+                       relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, j, &rv);
+                       relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, j, &rvo);
+
+                       if (!lv.compare(rv) && !lvo.compare(rvo)) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
+bool ctx::rule_manager::rule_item_equals(ctx::json& litem, ctx::json& ritem)
+{
+       // Compare item name
+       std::string lei, rei;
+       litem.get(NULL, CT_RULE_EVENT_ITEM, &lei);
+       ritem.get(NULL, CT_RULE_EVENT_ITEM, &rei);
+       if (lei.compare(rei))
+               return false;
+
+       // Compare option
+       ctx::json loption, roption;
+       std::string linst, rinst;
+       litem.get(NULL, CT_RULE_EVENT_OPTION, &loption);
+       ritem.get(NULL, CT_RULE_EVENT_OPTION, &roption);
+       linst = get_instance_name(lei, loption);
+       rinst = get_instance_name(rei, roption);
+       if (linst.compare(rinst))
+               return false;
+
+       int ledac, redac;
+       ledac = litem.array_get_size(NULL, CT_RULE_DATA_ARR);
+       redac = ritem.array_get_size(NULL, CT_RULE_DATA_ARR);
+       if (ledac != redac)
+               return false;
+
+       // Compare item operator;
+       if (ledac > 1 ) {
+               std::string leop, reop;
+               litem.get(NULL, CT_RULE_EVENT_OPERATOR, &leop);
+               ritem.get(NULL, CT_RULE_EVENT_OPERATOR, &reop);
+               if (leop.compare(reop))
+                       return false;
+       }
+
+       for (int i = 0; i < ledac; i++) {
+               bool found = false;
+               ctx::json lelem;
+               litem.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &lelem);
+
+               for (int j = 0; j < ledac; j++) {
+                       ctx::json relem;
+                       ritem.get_array_elem(NULL, CT_RULE_DATA_ARR, j, &relem);
+
+                       if (rule_data_arr_elem_equals(lelem, relem)) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
+bool ctx::rule_manager::rule_equals(ctx::json& lrule, ctx::json& rrule)
+{
+       // Compare event
+       ctx::json le, re;
+       lrule.get(NULL, CT_RULE_EVENT, &le);
+       rrule.get(NULL, CT_RULE_EVENT, &re);
+       if (!rule_item_equals(le, re))
+               return false;
+
+       // Compare conditions
+       int lcc, rcc;
+       lcc = lrule.array_get_size(NULL, CT_RULE_CONDITION);
+       rcc = rrule.array_get_size(NULL, CT_RULE_CONDITION);
+       if (lcc != rcc)
+               return false;
+
+       if (lcc > 1) {
+               std::string lop, rop;
+               lrule.get(NULL, CT_RULE_OPERATOR, &lop);
+               rrule.get(NULL, CT_RULE_OPERATOR, &rop);
+               if (lop.compare(rop))
+                       return false;
+       }
+
+       for (int i = 0; i < lcc; i++) {
+               bool found = false;
+               ctx::json lc;
+               lrule.get_array_elem(NULL, CT_RULE_CONDITION, i, &lc);
+
+               for (int j = 0; j < lcc; j++) {
+                       ctx::json rc;
+                       rrule.get_array_elem(NULL, CT_RULE_CONDITION, j, &rc);
+
+                       if (rule_item_equals(lc, rc)) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       return false;
+       }
+
+       // Compare action
+       std::string laction, raction;
+       lrule.get(NULL, CT_RULE_ACTION, &laction);
+       rrule.get(NULL, CT_RULE_ACTION, &raction);
+       if (laction.compare(raction))
+               return false;
+
+       return true;
+}
+
+int64_t ctx::rule_manager::get_duplicated_rule(std::string creator, std::string zone, ctx::json& rule)
+{
+       std::string q = "SELECT row_id, details FROM context_trigger_rule WHERE creator = '";
+       q += creator;
+       q += "' AND zone ='";
+       q += zone;
+       q += "'";
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_id, details by creator failed");
+
+       ctx::json r_details;
+       rule.get(NULL, CT_RULE_DETAILS, &r_details);
+       std::vector<json>::iterator vec_end = record.end();
+
+       for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+               std::string details;
+               ctx::json d_details;
+
+               elem.get(NULL, "details", &details);
+               d_details = details;
+
+               if (rule_equals(r_details, d_details)) {
+                       int64_t row_id;
+                       elem.get(NULL, "row_id", &row_id);
+                       return row_id;
+               }
+       }
+
+       return -1;
+}
+
+int ctx::rule_manager::verify_rule(ctx::json& rule, const char* app_id, const char* zone)
+{
+       ctx::json details;
+       rule.get(NULL, CT_RULE_DETAILS, &details);
+
+       std::string e_name;
+       rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name);
+
+       IF_FAIL_RETURN_TAG(c_monitor.is_supported(e_name, zone), ERR_NOT_SUPPORTED, _I, "Event(%s) is not supported", e_name.c_str());
+
+       if (app_id) {
+               if (!ctx::privilege_manager::is_allowed(app_id, e_name.c_str())) {
+                       _W("Permission denied for '%s'", e_name.c_str());
+                       return ERR_PERMISSION_DENIED;
+               }
+       }
+
+       ctx::json it;
+       for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){
+               std::string c_name;
+               it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name);
+
+               IF_FAIL_RETURN_TAG(c_monitor.is_supported(c_name, zone), ERR_NOT_SUPPORTED, _I, "Condition(%s) is not supported", c_name.c_str());
+
+               if (!ctx::privilege_manager::is_allowed(app_id, c_name.c_str())) {
+                       _W("Permission denied for '%s'", c_name.c_str());
+                       return ERR_PERMISSION_DENIED;
+               }
+       }
+
+       return ERR_NONE;
+}
+
+int ctx::rule_manager::add_rule(std::string creator, ctx::json rule, std::string zone, ctx::json* rule_id)
+{
+       // * Insert rule to DB
+       bool ret;
+       int64_t rid;
+
+       // Check if all items are supported && allowed to access
+       int err = verify_rule(rule, creator.c_str(), zone.c_str());
+       IF_FAIL_RETURN(err==ERR_NONE, err);
+
+       // Check if duplicated rule exits
+       if ((rid = get_duplicated_rule(creator, zone, rule)) > 0) {
+               // Save rule id
+               rule_id->set(NULL, CT_RULE_ID, rid);
+               return ERR_NONE;
+       }
+
+       // Insert rule to rule table, get rule id and save it to json parameter
+       ctx::json r_record;
+       std::string description;
+       ctx::json details;
+       rule.get(NULL, CT_RULE_DESCRIPTION, &description);
+       rule.get(NULL, CT_RULE_DETAILS, &details);
+       r_record.set(NULL, "creator", creator);
+       r_record.set(NULL, "description", description);
+       r_record.set(NULL, "details", details.str());
+       r_record.set(NULL, "zone", zone);
+       ret = db_manager::insert_sync(RULE_TABLE, r_record, &rid);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert rule to db failed");
+
+       // Save rule id
+       rule_id->set(NULL, CT_RULE_ID, rid);
+
+       // Insert event & conditions of a rule into each table
+       ctx::json e_record;
+       std::string e_name;
+       ctx::json e_option_j;
+       std::string e_inst;
+
+       rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name);
+       rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &e_option_j);
+       e_inst = get_instance_name(e_name, e_option_j);
+
+       e_record.set(NULL, "rule_id", rid);
+       e_record.set(NULL, "name", e_name);
+       e_record.set(NULL, "instance_name", e_inst);
+       e_record.set(NULL, "zone", zone);
+       ret = db_manager::insert(1, EVENT_TABLE, e_record, NULL);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert event to db failed");
+
+       ctx::json it;
+       for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){
+               ctx::json c_record;
+               std::string c_name;
+               ctx::json c_option;
+               char* c_option_str;
+               ctx::json tmp_option;
+               std::string c_inst;
+
+               it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name);
+               it.get(NULL, CT_RULE_CONDITION_OPTION, &tmp_option);
+               c_inst = get_instance_name(c_name, tmp_option);
+               c_option.set(NULL, CT_RULE_CONDITION_OPTION, tmp_option);
+               c_option_str = c_option.dup_cstr();
+
+               c_record.set(NULL, "rule_id", rid);
+               c_record.set(NULL, "name", c_name);
+               c_record.set(NULL, "option", (c_option_str)? c_option_str : "");
+               c_record.set(NULL, "instance_name", c_inst);
+               c_record.set(NULL, "zone", zone);
+
+               ret = db_manager::insert(2, CONDITION_TABLE, c_record, NULL);
+               IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert conditions to db failed");
+
+               free(c_option_str);
+       }
+
+       _D("Add rule%d succeeded", (int)rid);
+       return ERR_NONE;
+}
+
+
+int ctx::rule_manager::remove_rule(int rule_id)
+{
+       // Delete rule from DB
+       bool ret;
+
+       std::string query = DELETE_RULE_STATEMENT;
+       query += int_to_string(rule_id);
+       std::vector<json> record;
+       ret = db_manager::execute_sync(query.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed");
+
+       return ERR_NONE;
+}
+
+
+int ctx::rule_manager::enable_rule(int rule_id)
+{
+       // Subscribe event
+       bool ret;
+       int error;
+       std::string id_str = int_to_string(rule_id);
+
+       // Get rule json by rule id;
+       std::string q1 = QUERY_RULE_AND_ZONE_BY_RULE_ID;
+       q1 += int_to_string(rule_id);
+       std::vector<json> rule_record;
+       ret = db_manager::execute_sync(q1.c_str(), &rule_record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rule by rule id failed");
+       std::string r1;
+       std::string zone;
+       rule_record[0].get(NULL, "details", &r1);
+       rule_record[0].get(NULL, "zone", &zone);
+       ctx::json rule = r1;
+       ctx::json event;
+       rule.get(NULL, CT_RULE_EVENT, &event);
+
+       // Get event template by rule id
+       std::string q2 = QUERY_EVENT_TEMPLATE_BY_RULE_ID;
+       q2 += int_to_string(rule_id);
+       q2 += ")";
+       std::vector<json> etemplate_record;
+       ret = db_manager::execute_sync(q2.c_str(), &etemplate_record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query event template by rule id failed");
+       std::string r2;
+       etemplate_record[0].get(NULL, "j_template", &r2);
+       ctx::json etemplate = r2;
+
+       // Query name, instance name & template for conditions of the rule
+       std::string q3 = QUERY_NAME_INSTANCE_NAME_AND_TEMPLATE_BY_RULE_ID_STATEMENT;
+       q3 += id_str;
+       std::vector<json> cond_record;
+       ret = db_manager::execute_sync(q3.c_str(), &cond_record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query condition's names, instance names, templates by rule id failed");
+
+       ctx::json inst_names;
+       std::vector<json>::iterator vec_end = cond_record.end();
+       for (std::vector<json>::iterator vec_pos = cond_record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+
+               std::string cname;
+               std::string ciname;
+               std::string temp;
+               elem.get(NULL, "name", &cname);
+               elem.get(NULL, "instance_name", &ciname);
+               elem.get(NULL, "templates", &temp);
+               ctx::json ctemplate = temp;
+               ctemplate.set(NULL, "instance_name", ciname);
+               // TODO ctemplate (name, instance_name, attributes) now - it is based on form using when templates are inserted into db when rebooting
+
+               // For defrule script generation
+               inst_names.set(NULL, cname.c_str(), ciname);
+
+               if (cname.compare(ciname) != 0) {
+                       if (!clips_h.find_instance(ciname)) {
+                               std::string makeinst_script = script_generator::generate_makeinstance(&ctemplate);
+                               error = clips_h.make_instance(makeinst_script);
+                               IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Add condition instance([%s]) failed", ciname.c_str());
+
+                               cond_cnt_map[ciname] = 1;
+                       } else {
+                               cond_cnt_map[ciname]++;
+                       }
+               }
+       }
+
+       // Subscribe event
+       std::string ename;
+       etemplate.get(NULL, "name", &ename);
+
+       //TODO: set the proper zone
+       error = c_monitor.subscribe(rule_id, ename, event, zone.c_str());
+       IF_FAIL_RETURN(error == ERR_NONE, ERR_OPERATION_FAILED);
+
+       // Generate defrule script and execute it
+       std::string script = script_generator::generate_defrule(id_str, etemplate, rule, &inst_names, zone);
+       error = clips_h.define_rule(script);
+       IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defrule failed");
+
+       // Update db to set 'enabled'
+       std::string q4 = UPDATE_RULE_ENABLED_STATEMENT;
+       q4 += id_str;
+       std::vector<json> record;
+       ret = db_manager::execute_sync(q4.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed");
+
+       _D(YELLOW("Enable Rule%d succeeded"), rule_id);
+
+       return ERR_NONE;
+}
+
+std::string ctx::rule_manager::get_instance_name(std::string name, ctx::json& option)
+{
+       std::string inst_name = name;
+
+       // Get template for the option
+       std::string q = "SELECT j_template FROM context_trigger_template WHERE name = '";
+       q += name;
+       q += "'";
+       std::vector<json> template_record;
+       db_manager::execute_sync(q.c_str(), &template_record);
+
+       std::string ct_str;
+       template_record[0].get(NULL, "j_template", &ct_str);
+       ctx::json j_template = ct_str;
+
+       std::string option_key;
+       for (int i = 0; j_template.get_array_elem(NULL, "option", i, &option_key); i++) {
+               std::string val_str;
+               int val;
+               if (option.get(NULL, option_key.c_str(), &val_str)) {
+                       inst_name += INSTANCE_NAME_DELIMITER;
+                       inst_name += val_str;
+               } else if (option.get(NULL, option_key.c_str(), &val)) {
+                       inst_name += INSTANCE_NAME_DELIMITER;
+                       inst_name += int_to_string(val);
+               } else {
+                       inst_name += INSTANCE_NAME_DELIMITER;
+               }
+       }
+
+       return inst_name;
+}
+
+int ctx::rule_manager::disable_rule(int rule_id)
+{
+       int error;
+       bool ret;
+
+       // For event with options
+       // Get rule json by rule id;
+       std::string q1 = QUERY_RULE_AND_ZONE_BY_RULE_ID;
+       q1 += int_to_string(rule_id);
+       std::vector<json> rule_record;
+       ret = db_manager::execute_sync(q1.c_str(), &rule_record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rule by rule id failed");
+       std::string r1;
+       std::string zone;
+       rule_record[0].get(NULL, "details", &r1);
+       rule_record[0].get(NULL, "zone", &zone);
+       ctx::json rule = r1;
+       ctx::json event;
+       rule.get(NULL, CT_RULE_EVENT, &event);
+       std::string ename;
+       event.get(NULL, CT_RULE_EVENT_ITEM, &ename);
+
+       // Unsubscribe event
+       error = c_monitor.unsubscribe(rule_id, ename, event, zone.c_str());
+       IF_FAIL_RETURN(error == ERR_NONE, ERR_OPERATION_FAILED);
+
+       // Undef rule in clips
+       std::string id_str = int_to_string(rule_id);
+       std::string script = script_generator::generate_undefrule(id_str);
+       error = clips_h.route_string_command(script);
+       IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Undefrule failed");
+
+       // Update db to set 'disabled'
+       std::string q2 = UPDATE_RULE_DISABLED_STATEMENT;
+       q2 += id_str;
+       std::vector<json> record;
+       ret = db_manager::execute_sync(q2.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed");
+
+       // Remove condition instances TODO
+       std::string q3 = "SELECT name, instance_name FROM context_trigger_condition WHERE rule_id = ";
+       q3 += id_str;
+       std::vector<json> name_record;
+       ret = db_manager::execute_sync(q3.c_str(), &name_record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query condition's name, instance names by rule id failed");
+
+       std::vector<json>::iterator vec_end = name_record.end();
+       for (std::vector<json>::iterator vec_pos = name_record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+
+               std::string cname;
+               std::string ciname;
+               elem.get(NULL, "name", &cname);
+               elem.get(NULL, "instance_name", &ciname);
+
+               if (cname.compare(ciname) != 0) {
+                       cond_cnt_map[ciname]--;
+
+                       if (cond_cnt_map[ciname] == 0) {
+                               error = clips_h.unmake_instance(ciname);
+                               IF_FAIL_RETURN(error == ERR_NONE, error);
+
+                               cond_cnt_map.erase(ciname);
+                       }
+               }
+       }
+
+       return ERR_NONE;
+}
+
+void ctx::rule_manager::make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption)
+{
+       std::string option_key;
+       for (int i = 0; ctemplate.get_array_elem(NULL, "option", i, &option_key); i++) {
+               std::string coption_valstr;
+               if (coption->get(NULL, option_key.c_str(), &coption_valstr)) {
+                       if (coption_valstr.find(EVENT_KEY_PREFIX) == 0) {
+                               std::string event_key = coption_valstr.substr(1, coption_valstr.length() - 1);
+
+                               std::string e_valstr;
+                               int e_val;
+                               if (edata.get(NULL, event_key.c_str(), &e_valstr)) {
+                                       coption->set(NULL, option_key.c_str(), e_valstr);
+                               } else if (edata.get(NULL, event_key.c_str(), &e_val)) {
+                                       coption->set(NULL, option_key.c_str(), e_val);
+                               }
+                       }
+               }
+       }
+}
+
+void ctx::rule_manager::on_event_received(std::string item, ctx::json option, ctx::json data, std::string zone)
+{
+       _D(YELLOW("Event(%s(%s) - %s) is invoked."), item.c_str(), option.str().c_str(), data.str().c_str());
+       // TODO: Check permission of an event(item), if permission denied, return
+
+       int err;
+       bool ret;
+       // Set the zone
+       ret = clips_h.set_global_variable_string("zone", zone);
+       if (!ret) {
+               _E("Set clips zone to %s failed", zone.c_str());
+               return;
+       }
+
+       // Generate event fact script
+       std::string q1 = "SELECT j_template FROM context_trigger_template WHERE name = '";
+       q1 += item;
+       q1 += "'";
+       std::vector<json> etemplate_record;
+       db_manager::execute_sync(q1.c_str(), &etemplate_record);
+       std::string r1;
+       etemplate_record[0].get(NULL, "j_template", &r1);
+       ctx::json etemplate = r1;
+
+       std::string eventfact_str = script_generator::generate_fact(item, etemplate, option, data);
+
+       // Get Conditions template of invoked event (db query)
+       std::string e_inst = get_instance_name(item, option);
+       std::string query = QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT;
+       query += e_inst;
+       query += "' AND zone = '";
+       query += zone;
+       query += "'))";
+       std::vector<json> conds;
+       ret = db_manager::execute_sync(query.c_str(), &conds);
+       IF_FAIL_VOID_TAG(ret, _E, "Query condition templates of invoked event failed");
+
+       int cond_num = conds.size();
+       for (int i = 0; i < cond_num; i++) {
+               std::string temp;
+               conds[i].get(NULL, "j_template", &temp);
+               ctx::json ctemplate = temp;
+
+               std::string cname;
+               conds[i].get(NULL, "name", &cname);
+
+               std::string ciname;
+               conds[i].get(NULL, "instance_name", &ciname);
+
+               std::string coption_str;
+               conds[i].get(NULL, "option", &coption_str);
+               ctx::json coption = NULL;
+               if (!coption_str.empty()) {
+                       ctx::json coption_tmp = coption_str;
+                       coption_tmp.get(NULL, CT_RULE_CONDITION_OPTION, &coption);
+               }
+
+               // Check if the condition uses event data key as an option
+               if (ciname.find(EVENT_KEY_PREFIX) != std::string::npos) {
+                       make_condition_option_based_on_event_data(ctemplate, data, &coption);
+               }
+
+               // TODO: Check permission of a condition(cname), if permission granted, read condition data. (or, condition data should be empty json)
+
+               //      Get Context Data and Set the proper zone.
+               ctx::json condition_data;
+               err = c_monitor.read(cname, coption, zone.c_str(), &condition_data);
+               if (err != ERR_NONE)
+                       return;
+               _D(YELLOW("Condition(%s(%s) - %s)."), cname.c_str(), coption.str().c_str(), condition_data.str().c_str());
+
+               // Generate ModifyInstance script
+               std::string modifyinst_script = script_generator::generate_modifyinstance(ciname, ctemplate, condition_data);
+
+               err = clips_h.route_string_command(modifyinst_script);
+               IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Modify condition instance failed");
+       }
+
+       // Add fact and Run environment
+       err = clips_h.add_fact(eventfact_str);
+       IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Assert event fact failed");
+
+       err = clips_h.run_environment();
+       IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Run environment failed");
+
+       // Retract event fact
+       std::string retract_command = "(retract *)";
+       err = clips_h.route_string_command(retract_command);
+       IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Retract event fact failed");
+}
+
+static void trigger_action_app_control(ctx::json& action, std::string zone)
+{
+       int error;
+       std::string appctl_str;
+       action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str);
+
+       char* str = static_cast<char*>(malloc(appctl_str.length()));
+       if (str == NULL) {
+               _E("Memory allocation failed");
+               return;
+       }
+       appctl_str.copy(str, appctl_str.length(), 0);
+       bundle_raw* encoded = reinterpret_cast<unsigned char*>(str);
+       bundle* appctl_bundle = bundle_decode(encoded, appctl_str.length());
+
+       app_control_h app = NULL;
+       app_control_create(&app);
+       app_control_import_from_bundle(app, appctl_bundle);
+
+       char* op;
+       app_control_get_operation(app, &op);
+       app_control_set_operation(app, APP_SVC_OPERATION_JUMP);
+       app_control_add_extra_data(app, APP_SVC_K_JUMP_ZONE_NAME, zone.c_str());
+       if (op != NULL) {
+               app_control_add_extra_data(app, APP_SVC_K_JUMP_ORIGIN_OPERATION, op);
+       }
+
+       error = app_control_send_launch_request(app, NULL, NULL);
+       if (error != APP_CONTROL_ERROR_NONE) {
+               _E("Launch request failed(%d)", error);
+       } else {
+               _D("Launch request succeeded");
+       }
+       bundle_free(appctl_bundle);
+       free(str);
+       app_control_destroy(app);
+}
+
+static void trigger_action_notification(ctx::json& action, std::string creator, std::string zone)
+{
+       int error;
+       notification_h notification = notification_create(NOTIFICATION_TYPE_NOTI);
+       std::string title;
+       if (action.get(NULL, CT_RULE_ACTION_NOTI_TITLE, &title)) {
+               error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_TITLE, title.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE);
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set notification title failed(%d)", error);
+               }
+       }
+
+       std::string content;
+       if (action.get(NULL, CT_RULE_ACTION_NOTI_CONTENT, &content)) {
+               error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_CONTENT, content.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE);
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set notification contents failed(%d)", error);
+               }
+       }
+
+       std::string image_path;
+       if (action.get(NULL, CT_RULE_ACTION_NOTI_ICON_PATH, &image_path)) {
+               error = notification_set_image(notification, NOTIFICATION_IMAGE_TYPE_ICON, image_path.c_str());
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set notification icon image failed(%d)", error);
+               }
+       }
+
+       std::string appctl_str;
+       char* str = NULL;
+       bundle_raw* encoded = NULL;
+       bundle* appctl_bundle = NULL;
+       app_control_h app = NULL;
+       if (action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str)) {
+               str = static_cast<char*>(malloc(appctl_str.length()));
+               if (str == NULL) {
+                       _E("Memory allocation failed");
+                       notification_free(notification);
+                       return;
+               }
+               appctl_str.copy(str, appctl_str.length(), 0);
+               encoded = reinterpret_cast<unsigned char*>(str);
+               appctl_bundle = bundle_decode(encoded, appctl_str.length());
+
+               app_control_create(&app);
+               app_control_import_from_bundle(app, appctl_bundle);
+
+               error = notification_set_launch_option(notification, NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, app);
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set launch option failed(%d)", error);
+               }
+       }
+
+       error = notification_set_pkgname(notification, creator.c_str());
+       if (error != NOTIFICATION_ERROR_NONE) {
+               _E("Set pkgname(%s) failed(%d)", creator.c_str(), error);
+       }
+
+       ctx::scope_zone_joiner sz(zone.c_str());
+
+       error = notification_post(notification);
+       if (error != NOTIFICATION_ERROR_NONE) {
+               _E("Post notification failed(%d)", error);
+       } else {
+               _D("Post notification succeeded");
+       }
+
+       bundle_free(appctl_bundle);
+       free(str);
+       notification_free(notification);
+       if (app) {
+               app_control_destroy(app);
+       }
+}
+
+void ctx::rule_manager::on_rule_triggered(int rule_id)
+{
+       _D(YELLOW("Rule%d is triggered"), rule_id);
+
+       std::string q = "SELECT details, creator, zone FROM context_trigger_rule WHERE row_id =";
+       q += int_to_string(rule_id);
+
+       std::vector<json> record;
+       db_manager::execute_sync(q.c_str(), &record);
+
+       std::string details_str;
+       record[0].get(NULL, "details", &details_str);
+       ctx::json details = details_str;
+       ctx::json action;
+       details.get(NULL, CT_RULE_ACTION, &action);
+       std::string zone;
+       record[0].get(NULL, "zone", &zone);
+
+       std::string type;
+       if (action.get(NULL, CT_RULE_ACTION_TYPE, &type)) {
+               if (type.compare(CT_RULE_ACTION_TYPE_APP_CONTROL) == 0) {
+                       trigger_action_app_control(action, zone);
+               } else if (type.compare(CT_RULE_ACTION_TYPE_NOTIFICATION) == 0) {
+                       std::string creator;
+                       record[0].get(NULL, "creator", &creator);
+                       trigger_action_notification(action, creator, zone);
+               }
+       }
+}
+
+int ctx::rule_manager::check_rule(std::string creator, int rule_id)
+{
+       // Get creator app id
+       std::string q = "SELECT creator FROM context_trigger_rule WHERE row_id =";
+       q += int_to_string(rule_id);
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query creator by rule id failed");
+
+       if (record.size() == 0) {
+               return ERR_NO_DATA;
+       }
+
+       std::string c;
+       record[0].get(NULL, "creator", &c);
+
+       if (c.compare(creator) == 0){
+               return ERR_NONE;
+       }
+
+       return ERR_NO_DATA;
+}
+
+bool ctx::rule_manager::is_rule_enabled(int rule_id)
+{
+       std::string q = "SELECT enabled FROM context_trigger_rule WHERE row_id =";
+       q += int_to_string(rule_id);
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query enabled by rule id failed");
+
+       int enabled;
+       record[0].get(NULL, "enabled", &enabled);
+
+       if (enabled == 1)
+               return true;
+       else
+               return false;
+}
+
+int ctx::rule_manager::get_rule_by_id(std::string creator, int rule_id, ctx::json* request_result)
+{
+//     std::string q = "SELECT description, details FROM context_trigger_rule WHERE (creator = '";
+       std::string q = "SELECT description FROM context_trigger_rule WHERE (creator = '";
+       q += creator;
+       q += "') and (row_id = ";
+       q += int_to_string(rule_id);
+       q += ")";
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Query rule by rule id failed");
+
+       if (record.size() == 0) {
+               return ERR_NO_DATA;
+       } else if (record.size() != 1) {
+               return ERR_OPERATION_FAILED;
+       }
+
+       std::string description;
+//     std::string details;
+       record[0].get(NULL, "description", &description);
+//     record[0].get(NULL, "details", &details);
+
+       (*request_result).set(NULL, CT_RULE_ID, rule_id);
+       (*request_result).set(NULL, CT_RULE_DESCRIPTION, description);
+//     (*request_result).set(NULL, CT_RULE_DETAILS, details);
+
+       return ERR_NONE;
+}
+
+int ctx::rule_manager::get_rule_ids(std::string creator, ctx::json* request_result)
+{
+       (*request_result) = "{ \"" CT_RULE_ARRAY_ENABLED "\" : [ ] , \"" CT_RULE_ARRAY_DISABLED "\" : [ ] }";
+
+       std::string q = "SELECT row_id, enabled FROM context_trigger_rule WHERE (creator = '";
+       q += creator;
+       q += "')";
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rules failed");
+
+       std::vector<json>::iterator vec_end = record.end();
+       for (std::vector<json>::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) {
+               ctx::json elem = *vec_pos;
+               std::string id;
+               int enabled;
+
+               elem.get(NULL, "row_id", &id);
+               elem.get(NULL, "enabled", &enabled);
+
+               if (enabled == 1) {
+                       (*request_result).array_append(NULL, CT_RULE_ARRAY_ENABLED, string_to_int(id));
+               } else if (enabled == 0) {
+                       (*request_result).array_append(NULL, CT_RULE_ARRAY_DISABLED, string_to_int(id));
+               }
+       }
+
+       return ERR_NONE;
+}
diff --git a/src/context_trigger/rule_manager.h b/src/context_trigger/rule_manager.h
new file mode 100644 (file)
index 0000000..e6b1557
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+* context-service
+*
+* Copyright (c) 2014 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+#ifndef __RULE_MANAGER_H__
+#define __RULE_MANAGER_H__
+
+#include "clips_handler.h"
+#include "context_monitor.h"
+
+namespace ctx {
+
+       class json;
+       class context_trigger;
+       class fact_reader;
+
+       class rule_manager {
+               public:
+                       rule_manager();
+                       ~rule_manager();
+                       bool init(ctx::context_trigger* tr, ctx::fact_reader* fr);
+                       int add_rule(std::string creator, ctx::json rule, std::string zone, ctx::json* rule_id);
+                       int remove_rule(int rule_id);
+                       int enable_rule(int rule_id);
+                       int disable_rule(int rule_id);
+                       int get_rule_by_id(std::string creator, int rule_id, ctx::json* request_result);
+                       int get_rule_ids(std::string creator, ctx::json* request_result);
+                       int check_rule(std::string creator, int rule_id);
+                       bool is_rule_enabled(int rule_id);
+
+                       void on_event_received(std::string item, ctx::json option, ctx::json data, std::string zone);
+                       void on_rule_triggered(int rule_id);
+
+               private:
+                       clips_handler clips_h;
+                       context_monitor c_monitor;
+
+                       bool reenable_rule(void);
+                       int verify_rule(ctx::json& rule, const char* app_id, const char* zone);
+                       int64_t get_duplicated_rule(std::string creator, std::string zone, ctx::json& rule);
+                       bool rule_data_arr_elem_equals(ctx::json& lelem, ctx::json& relem);
+                       bool rule_item_equals(ctx::json& litem, ctx::json& ritem);
+                       bool rule_equals(ctx::json& lrule, ctx::json& rrule);
+                       std::string get_instance_name(std::string name, ctx::json& condition);
+                       void make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption);
+
+                       std::map<std::string, int> cond_cnt_map; // <condition instance name, count>
+
+   };  /* class rule_manager */
+
+}      /* namespace ctx */
+
+#endif /* End of __RULE_MANAGER_H__ */
diff --git a/src/context_trigger/script_generator.cpp b/src/context_trigger/script_generator.cpp
new file mode 100644 (file)
index 0000000..ade39ef
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * context-service
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <sstream>
+#include <set>
+#include <glib.h>
+#include <string>
+#include <json.h>
+#include <types_internal.h>
+#include <context_trigger_types_internal.h>
+#include "script_generator.h"
+#include "timer_types.h"
+
+#define EVENT_WEEKDAY "(or (eq ?DayOfWeek \"Mon\") (eq ?DayOfWeek \"Tue\") (eq ?DayOfWeek \"Wed\") (eq ?DayOfWeek \"Thu\") (eq ?DayOfWeek \"Fri\"))"
+#define EVENT_WEEKEND "(or (eq ?DayOfWeek \"Sat\") (eq ?DayOfWeek \"Sun\"))"
+#define CONDITION_WEEKDAY "(or (eq (send [%s] get-DayOfWeek) \"Mon\") (eq (send [%s] get-DayOfWeek) \"Tue\") (eq (send [%s] get-DayOfWeek) \"Wed\") (eq (send [%s] get-DayOfWeek) \"Thu\") (eq (send [%s] get-DayOfWeek) \"Fri\"))"
+#define CONDITION_WEEKEND "(or (eq (send [%s] get-DayOfWeek) \"Sat\") (eq (send [%s] get-DayOfWeek) \"Sun\"))"
+
+static std::string generate_initial_fact(ctx::json event_template, ctx::json option);
+static std::string generate_event_data(ctx::json event);
+static std::string generate_condition_data(std::string rule_id, ctx::json conditions, std::string rule_op, ctx::json* inst_names);
+
+static std::string int_to_string(int i)
+{
+       std::ostringstream convert;
+       convert << i;
+       std::string str = convert.str();
+       return str;
+}
+
+static std::string convert_condition_weekday_weekend(std::string inst_name, std::string day)
+{
+       std::string buf = (day.compare(TIMER_WEEKDAY) == 0)? CONDITION_WEEKDAY : CONDITION_WEEKEND;
+
+       size_t pos = 0;
+       while((pos = buf.find("%s", pos)) != std::string::npos) {
+               buf.replace(pos, 2, inst_name);
+               pos += inst_name.length();
+       }
+
+       return buf;
+}
+
+std::string ctx::script_generator::generate_deftemplate(ctx::json* item)
+{
+       std::string script;
+
+       std::string name;
+       item->get(NULL, "name", &name);
+
+       std::set<std::string> slot;
+
+       std::string opt_name;
+       for (int i = 0; item->get_array_elem(NULL, "option", i, &opt_name); i++) {
+               slot.insert(opt_name);
+       }
+
+       std::string attr_name;
+       for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) {
+               slot.insert(attr_name);
+       }
+
+       //template name is "itemname"
+       script = "(deftemplate ";
+       script += name;
+       script += " ";
+
+       for (std::set<std::string>::iterator it = slot.begin(); it != slot.end(); ++it){
+               script += "(slot ";
+               script += *it;
+               script += " (default null))";
+       }
+       script += ")";
+
+//     _D("Deftemplate script is generated: %s", script.c_str());
+       script += "\n";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_defclass(ctx::json* item)
+{
+       std::string script;
+
+       std::string name;
+       item->get(NULL, "name", &name);
+
+       //class name is "C.itemname"
+       script = "(defclass C.";
+       script += name;
+       script += " (is-a USER) (role concrete) ";
+
+       std::string attr_name;
+       for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) {
+               script += "(slot ";
+               script += attr_name;
+               script += " (create-accessor read-write))";
+       }
+       script += ")";
+
+//     _D("Defclass script is generated: %s", script.c_str());
+       script += "\n";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_makeinstance(ctx::json* item)
+{
+       std::string script;
+
+       std::string name;
+       item->get(NULL, "name", &name);
+
+       std::string instance_name;
+       if (!item->get(NULL, "instance_name", &instance_name)) {
+               instance_name = name;
+       }
+
+       //instance name is "[itemname]"
+       script = "([";
+       script += instance_name;
+       script += "] of C.";
+       script += name;
+
+       std::string attr_name;
+       for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) {
+               script += " (";
+               script += attr_name;
+               script += " 0)";
+       }
+       script += ")";
+
+//     _D("Makeinstance script is generated: %s", script.c_str());
+       script += "\n";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_undefrule(std::string rule_id)
+{
+       std::string script;
+       script = "(undefrule rule";
+       script += rule_id;
+       script += ")";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_defrule(std::string rule_id, ctx::json event_template, ctx::json rule, ctx::json* inst_names, std::string zone)
+{
+       std::string script;
+       ctx::json option = NULL;
+       rule.get(CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &option);
+
+       script = "(defrule rule";
+       script += rule_id;
+       script += " ";
+
+       script += generate_initial_fact(event_template, option);
+       script += " => ";
+
+       int eventdata_num = rule.array_get_size(CT_RULE_EVENT, CT_RULE_DATA_ARR);
+       int condition_num = rule.array_get_size(NULL, CT_RULE_CONDITION);
+
+       if (condition_num > 0) {
+               // case1: condition exists
+               script += "(if (and (eq ?*zone* \"";
+               script += zone;
+               script += "\") ";
+
+               if (eventdata_num > 0) {
+                       ctx::json event;
+                       rule.get(NULL, CT_RULE_EVENT, &event);
+                       script += generate_event_data(event);
+               }
+
+               // condition
+               ctx::json conditions;
+               rule.get(NULL, CT_RULE_CONDITION, &conditions);
+               std::string rule_op;
+               rule.get(NULL, CT_RULE_OPERATOR, &rule_op);
+               script += generate_condition_data(rule_id, conditions, rule_op, inst_names);
+
+               script += ")";
+               script += " then ";
+               script += " (execute_action rule";
+               script += rule_id;
+               script += "))";
+       } else if (eventdata_num > 0) {
+               // case2: no conditions, but event data
+               ctx::json event;
+               rule.get(NULL, CT_RULE_EVENT, &event);
+
+               script += "(if (and (eq ?*zone* \"";
+               script += zone;
+               script += "\") ";
+               script += generate_event_data(event);
+               script += ") then ";
+               script += " (execute_action rule";
+               script += rule_id;
+               script += "))";
+       } else {
+               // case3: only action
+               script += "if (eq ?*zone* \"";
+               script += zone;
+               script += "\") then (execute_action rule";
+               script += rule_id;
+               script += ")";
+       }
+
+       script += ")";
+       _D("Defrule script generated: %s", script.c_str());
+       return script;
+}
+
+std::string generate_initial_fact(ctx::json event_template, ctx::json option)
+{
+       std::string script = "(";
+       std::string e_name;
+       event_template.get(NULL, "name", &e_name);
+
+       script += e_name;
+       script += " ";
+
+       // options
+       std::string opt_key;
+       for (int i = 0; event_template.get_array_elem(NULL, "option", i, &opt_key); i++) {
+               script += "(";
+               script += opt_key;
+               script += " ";
+
+               std::string val_str;
+               int val;
+               if (option.get(NULL, opt_key.c_str(), &val_str)) {
+                       script += val_str;
+               } else if (option.get(NULL, opt_key.c_str(), &val)) {
+                       script += int_to_string(val);
+               } else {
+                       script += "?";
+                       script += opt_key;
+               }
+
+               script += ")";
+       }
+
+       // attributes
+       std::string attr;
+       for (int i = 0; event_template.get_array_elem(NULL, "attributes", i, &attr); i++) {
+               script += "(";
+               script += attr;
+               script += " ?";
+               script += attr;
+               script += ")";
+       }
+       script += ")";
+
+//     _D("Initial event fact is %s", script.c_str());
+       return script;
+}
+
+std::string generate_event_data(ctx::json event)
+{
+       std::string ename;
+       event.get(NULL, CT_RULE_EVENT_ITEM, &ename);
+
+       std::string script;
+       int edata_count = event.array_get_size(NULL, CT_RULE_DATA_ARR);
+
+       if (edata_count > 1) {
+               std::string item_op;
+               event.get(NULL, CT_RULE_EVENT_OPERATOR, &item_op);
+               script += "(";
+               script += item_op;
+               script += " ";
+       }
+
+       ctx::json edata;
+       for (int i = 0; event.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &edata); i++) {
+               std::string key_name;
+               if (ename.compare(TIMER_EVENT_SUBJECT) == 0) {
+                       edata.get(NULL, CT_RULE_DATA_KEY, &key_name);
+               }
+
+               int valuecount = edata.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
+               int opcount = edata.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
+               std::string key;
+               edata.get(NULL, CT_RULE_DATA_KEY, &key);
+
+               if (valuecount != opcount) {
+                       _E("Invalid event data. (Value count:%d, Operator count: %d)", valuecount, opcount);
+                       return "";
+               }
+
+               if (valuecount > 1) {
+                       script += "(";
+
+                       std::string key_op;
+                       edata.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &key_op);
+                       script += key_op;
+                       script += " ";
+               }
+
+               for (int j = 0; j < valuecount; j++){
+                       std::string val_op;
+                       std::string val;
+                       edata.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, j, &val_op);
+                       edata.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, j, &val);
+
+                       if (key_name.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) {
+                               if (val.compare("\"" TIMER_WEEKDAY "\"") == 0) {
+                                       script += (val_op.compare("eq") == 0)? EVENT_WEEKDAY : EVENT_WEEKEND;
+                                       continue;
+                               } else if (val.compare("\"" TIMER_WEEKEND "\"") == 0) {
+                                       script += (val_op.compare("eq") == 0)? EVENT_WEEKEND : EVENT_WEEKDAY;
+                                       continue;
+                               }
+                       }
+
+                       script += "(";
+                       script += val_op;
+                       script += " ?";
+                       script += key;
+                       script += " ";
+                       script += val;
+                       script += ")";
+               }
+
+               if (valuecount > 1) {
+                       script += ")";
+               }
+       }
+
+       if (edata_count > 1) {
+               script += ")";
+       }
+
+       return script;
+}
+
+std::string generate_condition_data(std::string rule_id, ctx::json conditions, std::string rule_op, ctx::json* inst_names)
+{
+       std::string script;
+
+       ctx::json conds;
+       conds.set(NULL, CT_RULE_CONDITION, conditions);
+
+       int conds_count = conds.array_get_size(NULL, CT_RULE_CONDITION);
+       if (conds_count > 1) {
+               script += "(";
+               script += rule_op;      // operator between each condition item
+               script += " ";
+       }
+
+       ctx::json it;
+       for (int i = 0; conds.get_array_elem(NULL, CT_RULE_CONDITION, i, &it); i++) {
+               std::string cond_name;
+               it.get(NULL, CT_RULE_CONDITION_ITEM, &cond_name);
+
+               std::string inst_name;
+               (inst_names)->get(NULL, cond_name.c_str(), &inst_name);
+
+               int data_count = it.array_get_size(NULL, CT_RULE_DATA_ARR);
+
+               if (data_count > 1) {
+                       std::string item_op;
+                       it.get(NULL, CT_RULE_CONDITION_OPERATOR, &item_op);
+                       script += "(";
+                       script += item_op; // operator between data keys of a condition item
+                       script += " ";
+               }
+
+               ctx::json data;
+               for (int j = 0; it.get_array_elem(NULL, CT_RULE_DATA_ARR, j, &data); j++) {
+                       std::string key_name;
+                       data.get(NULL, CT_RULE_DATA_KEY, &key_name);
+                       int dval_count = data.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR);
+                       int dvalop_count = data.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR);
+
+                       if (dval_count != dvalop_count) {
+                               _E("Invalid condition data. (Data value count %d, Data value operator count %d)", dval_count, dvalop_count);
+                       }
+
+                       if (dval_count > 1) {
+                               std::string dkey_op;
+                               data.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &dkey_op);
+
+                               script += "(";
+                               script += dkey_op; // operator between comparison data values of a condition data key
+                               script += " " ;
+                       }
+
+                       for (int k = 0; k < dval_count; k++) {
+                               std::string dval_op;
+                               std::string dval;
+                               data.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, k, &dval_op);
+                               data.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, k, &dval);
+
+                               if (inst_name.compare(TIMER_CONDITION_SUBJECT) == 0 && key_name.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) {
+                                       if (dval.compare("\"" TIMER_WEEKDAY "\"") == 0) {
+                                               script += convert_condition_weekday_weekend(inst_name, (dval_op.compare("eq") == 0)? TIMER_WEEKDAY : TIMER_WEEKEND);
+                                               continue;
+                                       } else if (dval.compare("\"" TIMER_WEEKEND "\"") == 0) {
+                                               script += convert_condition_weekday_weekend(inst_name, (dval_op.compare("eq") == 0)? TIMER_WEEKEND : TIMER_WEEKDAY);
+                                               continue;
+                                       }
+                               }
+
+                               script += "(";
+                               script += dval_op;
+                               script += " (send [";
+                               script += inst_name;
+                               script += "] get-";
+                               script += key_name;
+                               script += ") ";
+                               script += dval;
+                               script += ")";
+                       }
+
+                       if (dval_count > 1) {
+                               script += ")";
+                       }
+               }
+
+               if (data_count > 1 ) {
+                       script += ")";
+               }
+       }
+
+       if (conds_count > 1) {
+               script += ")";
+       }
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_fact(std::string item_name, ctx::json event_template, ctx::json option, ctx::json data)
+{
+       // Generate Fact script for invoked event
+       std::string script = "(";
+       script += item_name;
+       script += " ";
+
+       std::string opt_key;
+       std::string key;
+       std::string val_str;
+       int value;
+
+       for (int i = 0; event_template.get_array_elem(NULL, "option", i, &opt_key); i++) {
+               script += "(";
+               script += opt_key;
+               script += " ";
+               if (option.get(NULL, opt_key.c_str(), &val_str)) {      // string type data
+                       script += val_str;
+               } else if (option.get(NULL, opt_key.c_str(), &value)) { // integer type data
+                       script += int_to_string(value);
+               } else {
+                       script += "nil";
+               }
+               script += ")";
+       }
+
+       for (int i = 0; event_template.get_array_elem(NULL, "attributes", i, &key); i++) {
+               script += "(";
+               script += key;
+               script += " ";
+               if (data.get(NULL, key.c_str(), &val_str)) {    // string type data
+                       script += "\"" + val_str + "\"";
+               } else if (data.get(NULL, key.c_str(), &value)) {       // integer type data
+                       script += int_to_string(value);
+               } else {
+                       script += "nil";
+               }
+               script += ")";
+       }
+       script += ")";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_modifyinstance(std::string instance_name, ctx::json condition_template, ctx::json data)
+{
+       std::string script = "(modify-instance [";
+       script += instance_name;
+       script += "] ";
+
+       std::string key;
+       std::string val_str;
+       int value;
+       for (int i = 0; condition_template.get_array_elem(NULL, "attributes", i, &key); i++) {
+               if (data.get(NULL, key.c_str(), &val_str)) {    // string type data
+                       script += "(" + key + " \"" + val_str + "\")";
+               } else  if (data.get(NULL, key.c_str(), &value)) {      // integer type data
+                       script += "(" + key + " " + int_to_string(value) + ")";
+               }
+               // else continue;
+       }
+       script += ")";
+
+       return script;
+}
diff --git a/src/context_trigger/script_generator.h b/src/context_trigger/script_generator.h
new file mode 100644 (file)
index 0000000..b6fdbb9
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * context-service
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __CONTEXT_SCRIPT_GENERATOR_H__
+#define __CONTEXT_SCRIPT_GENERATOR_H__
+
+namespace ctx {
+
+       namespace script_generator {
+
+               std::string generate_deftemplate(ctx::json* item);
+               std::string generate_defclass(ctx::json* item);
+               std::string generate_makeinstance(ctx::json* item);
+               std::string generate_undefrule(std::string rule_id);
+               std::string generate_defrule(std::string rule_id, ctx::json event_template, ctx::json rule, ctx::json* inst_names, std::string zone);
+               std::string generate_fact(std::string item_name, ctx::json event_template, ctx::json option, ctx::json data);
+               std::string generate_modifyinstance(std::string instance_name, ctx::json condition_template, ctx::json data);
+
+       }
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_SCRIPT_GENERATOR_H__ */
diff --git a/src/context_trigger/timer.cpp b/src/context_trigger/timer.cpp
new file mode 100644 (file)
index 0000000..76999b6
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include <types_internal.h>
+#include <scope_mutex.h>
+#include <timer_mgr.h>
+#include "timer.h"
+#include "trigger.h"
+#include "timer_types.h"
+
+#define MAX_HOUR       24
+#define MAX_DAY                7
+
+using namespace ctx::timer_manager;
+static GMutex timer_mutex;
+
+ctx::trigger_timer::ref_count_array_s::ref_count_array_s()
+{
+       memset(count, 0, sizeof(int) * MAX_DAY);
+}
+
+ctx::trigger_timer::trigger_timer(ctx::context_trigger* tr, std::string z)
+       : trigger(tr)
+       , zone(z)
+{
+}
+
+ctx::trigger_timer::~trigger_timer()
+{
+       clear();
+}
+
+int ctx::trigger_timer::merge_day_of_week(int* ref_cnt)
+{
+       int day_of_week = 0;
+
+       for (int d = 0; d < MAX_DAY; ++d) {
+               if (ref_cnt[d] > 0) {
+                       day_of_week |= (0x01 << d);
+               }
+       }
+
+       return day_of_week;
+}
+
+bool ctx::trigger_timer::add(int minute, int day_of_week)
+{
+       IF_FAIL_RETURN_TAG(minute >=0 && minute < 1440 &&
+                       day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY,
+                       false, _E, "Invalid parameter");
+
+       ctx::scope_mutex sm(&timer_mutex);
+
+       ref_count_array_s &ref = ref_count_map[minute];
+
+       for (int d = 0; d < MAX_DAY; ++d) {
+               if ((day_of_week & (0x01 << d)) != 0) {
+                       ref.count[d] += 1;
+               }
+       }
+
+       return reset_timer(minute);
+}
+
+bool ctx::trigger_timer::remove(int minute, int day_of_week)
+{
+       IF_FAIL_RETURN_TAG(minute >=0 && minute < 1440 &&
+                       day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY,
+                       false, _E, "Invalid parameter");
+
+       ctx::scope_mutex sm(&timer_mutex);
+
+       ref_count_array_s &ref = ref_count_map[minute];
+
+       for (int d = 0; d < MAX_DAY; ++d) {
+               if ((day_of_week & (0x01 << d)) != 0 && ref.count[d] > 0) {
+                       ref.count[d] -= 1;
+               }
+       }
+
+       return reset_timer(minute);
+}
+
+bool ctx::trigger_timer::reset_timer(int minute)
+{
+       int day_of_week = merge_day_of_week(ref_count_map[minute].count);
+       timer_state_s &timer = timer_state_map[minute];
+
+       if (day_of_week == timer.day_of_week) {
+               /* Necessary timers are already running... */
+               return true;
+       }
+
+       if (day_of_week == 0 && timer.timer_id > 0) {
+               /* Turn off the timer at hour, if it is not necessray anymore. */
+               timer_manager::remove(timer.timer_id);
+               timer_state_map.erase(minute);
+               ref_count_map.erase(minute);
+               return true;
+       }
+
+       if (timer.timer_id > 0) {
+               /* Turn off the current timer, to set a new one. */
+               timer_manager::remove(timer.timer_id);
+               timer.timer_id = -1;
+               timer.day_of_week = 0;
+       }
+
+       /* Create a new timer, w.r.t. the new day_of_week value. */
+       int h = minute / 60;
+       int m = minute - h * 60;
+       int tid = timer_manager::set_at(h, m, day_of_week, this);
+       IF_FAIL_RETURN_TAG(tid > 0, false, _E, "Timer setting failed");
+
+       timer.timer_id = tid;
+       timer.day_of_week = day_of_week;
+
+       return true;
+}
+
+void ctx::trigger_timer::clear()
+{
+       ctx::scope_mutex sm(&timer_mutex);
+
+       for (timer_state_map_t::iterator it = timer_state_map.begin(); it != timer_state_map.end(); ++it) {
+               if (it->second.timer_id > 0) {
+                       timer_manager::remove(it->second.timer_id);
+               }
+       }
+
+       timer_state_map.clear();
+       ref_count_map.clear();
+}
+
+bool ctx::trigger_timer::on_timer_expired(int timer_id, void* user_data)
+{
+       time_t rawtime;
+       struct tm timeinfo;
+
+       time(&rawtime);
+       tzset();
+       localtime_r(&rawtime, &timeinfo);
+
+       int hour = timeinfo.tm_hour;
+       int min = timeinfo.tm_min;
+       int day_of_week = (0x01 << timeinfo.tm_wday);
+
+       on_timer_expired(hour, min, day_of_week);
+
+       return true;
+}
+
+void ctx::trigger_timer::on_timer_expired(int hour, int min, int day_of_week)
+{
+       _I("[Timer-%s] Time: %02d:%02d, Day of Week: %#x", zone.c_str(), hour, min, day_of_week);
+
+       ctx::json result;
+       result.set(NULL, TIMER_RESPONSE_KEY_TIME_OF_DAY, hour * 60 + min);
+       result.set(NULL, TIMER_RESPONSE_KEY_DAY_OF_WEEK, convert_day_of_week_to_string(day_of_week));
+
+       ctx::json dummy = NULL;
+       trigger->push_fact(TIMER_EVENT_REQ_ID, ERR_NONE, TIMER_EVENT_SUBJECT, dummy, result, zone.c_str());
+}
+
+int ctx::trigger_timer::get_day_of_month()
+{
+       time_t rawtime;
+       struct tm timeinfo;
+
+       time(&rawtime);
+       tzset();
+       localtime_r(&rawtime, &timeinfo);
+
+       int day_of_month = timeinfo.tm_mday;
+
+       return day_of_month;
+}
+
+std::string ctx::trigger_timer::get_day_of_week()
+{
+       time_t rawtime;
+       struct tm timeinfo;
+
+       time(&rawtime);
+       tzset();
+       localtime_r(&rawtime, &timeinfo);
+
+       int day_of_week = (0x01 << timeinfo.tm_wday);
+
+       return convert_day_of_week_to_string(day_of_week);
+}
+
+int ctx::trigger_timer::get_minute_of_day()
+{
+       time_t rawtime;
+       struct tm timeinfo;
+
+       time(&rawtime);
+       tzset();
+       localtime_r(&rawtime, &timeinfo);
+
+       int hour = timeinfo.tm_hour;
+       int minute = timeinfo.tm_min;
+
+       return hour * 60 + minute;
+}
+
+int ctx::trigger_timer::convert_string_to_day_of_week(std::string d)
+{
+       int day = 0;
+       d = d.substr(1, d.length() - 2);
+
+       if (d.compare(TIMER_SUN) == 0) {
+               day = SUN;
+       } else if (d.compare(TIMER_MON) == 0) {
+               day = MON;
+       } else if (d.compare(TIMER_TUE) == 0) {
+               day = TUE;
+       } else if (d.compare(TIMER_WED) == 0) {
+               day = WED;
+       } else if (d.compare(TIMER_THU) == 0) {
+               day = THU;
+       } else if (d.compare(TIMER_FRI) == 0) {
+               day = FRI;
+       } else if (d.compare(TIMER_SAT) == 0) {
+               day = SAT;
+       } else if (d.compare(TIMER_WEEKDAY) == 0) {
+               day = WEEKDAY;
+       } else if (d.compare(TIMER_WEEKEND) == 0) {
+               day = WEEKEND;
+       } else if (d.compare(TIMER_EVERYDAY) == 0) {
+               day = EVERYDAY;
+       }
+
+       return day;
+}
+
+std::string ctx::trigger_timer::convert_day_of_week_to_string(int d)
+{
+       std::string day;
+
+       if (d == SUN) {
+               day = TIMER_SUN;
+       } else if (d == MON) {
+               day = TIMER_MON;
+       } else if (d == TUE) {
+               day = TIMER_TUE;
+       } else if (d == WED) {
+               day = TIMER_WED;
+       } else if (d == THU) {
+               day = TIMER_THU;
+       } else if (d == FRI) {
+               day = TIMER_FRI;
+       } else if (d == SAT) {
+               day = TIMER_SAT;
+       } else if (d == WEEKDAY) {
+               day = TIMER_WEEKDAY;
+       } else if (d == WEEKEND) {
+               day = TIMER_WEEKEND;
+       } else if (d == EVERYDAY) {
+               day = TIMER_EVERYDAY;
+       }
+
+       return day;
+}
+
+bool ctx::trigger_timer::empty()
+{
+       return timer_state_map.empty();
+}
diff --git a/src/context_trigger/timer.h b/src/context_trigger/timer.h
new file mode 100644 (file)
index 0000000..3235abe
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_CONTEXT_TRIGGER_TIMER_H__
+#define __CONTEXT_CONTEXT_TRIGGER_TIMER_H__
+
+#include <timer_mgr.h>
+#include <timer_listener_iface.h>
+#include <string>
+#include <map>
+
+namespace ctx {
+
+       class context_trigger;
+
+       class trigger_timer : public timer_listener_iface {
+       private:
+               struct ref_count_array_s {
+                       int count[7];   /* reference counts for days of week*/
+                       ref_count_array_s();
+               };
+
+               struct timer_state_s {
+                       int timer_id;
+                       int day_of_week; /* day of week, merged into one integer */
+                       timer_state_s() : timer_id(-1), day_of_week(0) {}
+               };
+
+               typedef std::map<int, ref_count_array_s> ref_count_map_t;
+               typedef std::map<int, timer_state_s> timer_state_map_t;
+
+               ctx::context_trigger *trigger;
+               std::string zone;
+               ref_count_map_t ref_count_map;
+               timer_state_map_t timer_state_map;
+
+               int merge_day_of_week(int *ref_cnt);
+               bool reset_timer(int hour);
+               void on_timer_expired(int hour, int min, int day_of_week);
+
+       protected:
+               bool on_timer_expired(int timer_id, void *user_data);
+
+       public:
+               trigger_timer(ctx::context_trigger *tr, std::string z);
+               ~trigger_timer();
+
+               bool add(int minute, int day_of_week);
+               bool remove(int minute, int day_of_week);
+               void clear();
+               static int get_day_of_month();
+               static std::string get_day_of_week();
+               static int get_minute_of_day();
+               static int convert_string_to_day_of_week(std::string d);
+               static std::string convert_day_of_week_to_string(int d);
+               bool empty();
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_CONTEXT_TRIGGER_TIMER_H__ */
diff --git a/src/context_trigger/timer_types.h b/src/context_trigger/timer_types.h
new file mode 100644 (file)
index 0000000..250858a
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_TIMER_TYPES_H__
+#define __CONTEXT_TIMER_TYPES_H__
+
+#define TIMER_EVENT_SUBJECT "timer/event"
+#define TIMER_CONDITION_SUBJECT "timer/state"
+
+#define TIMER_RESPONSE_KEY_TIME_OF_DAY "TimeOfDay"
+#define TIMER_RESPONSE_KEY_DAY_OF_WEEK "DayOfWeek"
+#define TIMER_RESPONSE_KEY_DAY_OF_MONTH "DayOfMonth"
+
+#define TIMER_EVENT_REQ_ID -1
+
+#define TIMER_MON "Mon"
+#define TIMER_TUE "Tue"
+#define TIMER_WED "Wed"
+#define TIMER_THU "Thu"
+#define TIMER_FRI "Fri"
+#define TIMER_SAT "Sat"
+#define TIMER_SUN "Sun"
+#define TIMER_WEEKDAY "Weekday"
+#define TIMER_WEEKEND "Weekend"
+#define TIMER_EVERYDAY "Everyday"
+
+#endif //__CONTEXT_TIMER_TYPES_H__
diff --git a/src/context_trigger/trigger.cpp b/src/context_trigger/trigger.cpp
new file mode 100644 (file)
index 0000000..abde829
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <types_internal.h>
+#include <context_trigger_types_internal.h>
+#include "fact_reader.h"
+#include "trigger.h"
+#include "rule_manager.h"
+
+ctx::context_trigger::context_trigger()
+       : _reader(NULL)
+{
+}
+
+ctx::context_trigger::~context_trigger()
+{
+}
+
+bool ctx::context_trigger::init(ctx::context_manager_impl* mgr)
+{
+       // Do the necessary initialization process.
+       // This function is called from the main thread during the service launching process.
+       _reader = new(std::nothrow) fact_reader(mgr, this);
+       IF_FAIL_RETURN_TAG(_reader, false, _E, "Memory allocation failed");
+
+       _D("Starting Context Trigger Thread");
+       IF_FAIL_RETURN(start(), false);
+
+       push_thread_event(ETYPE_INITIALIZE, NULL);
+
+       return true;
+}
+
+void ctx::context_trigger::release()
+{
+       _D("Stopping Context Trigger Thread");
+       stop();
+
+       // Release the occupied resources.
+       // This function is called from the main thread during the service termination process.
+       delete _reader;
+       _reader = NULL;
+}
+
+void ctx::context_trigger::on_thread_event_popped(int type, void* data)
+{
+       switch (type) {
+               case ETYPE_REQUEST:
+                       IF_FAIL_VOID(data);
+                       process_request(static_cast<request_info*>(data));
+                       break;
+               case ETYPE_FACT:
+                       IF_FAIL_VOID(data);
+                       process_fact(static_cast<context_fact*>(data));
+                       break;
+               case ETYPE_INITIALIZE:
+                       process_initialize();
+                       break;
+               default:
+                       _W("Unknown event type");
+                       return;
+       }
+
+       delete_thread_event(type, data);
+}
+
+void ctx::context_trigger::delete_thread_event(int type, void* data)
+{
+       IF_FAIL_VOID(data);
+
+       switch (type) {
+               case ETYPE_REQUEST:
+                       {
+                               std::string subject = static_cast<ctx::request_info*>(data)->get_subject();
+                               if (subject != CONTEXT_TRIGGER_SUBJECT_ENABLE) {
+                                       delete (static_cast<request_info*>(data));
+                               }
+                       }
+                       return;
+               case ETYPE_FACT:
+                       delete (static_cast<context_fact*>(data));
+                       return;
+               case ETYPE_INITIALIZE:
+                       return;
+               default:
+                       _W("Unknown event type");
+                       return;
+       }
+}
+
+bool ctx::context_trigger::assign_request(ctx::request_info* request)
+{
+       std::string subject = request->get_subject();
+       if (subject != CONTEXT_TRIGGER_SUBJECT_ADD && subject != CONTEXT_TRIGGER_SUBJECT_REMOVE &&
+                       subject != CONTEXT_TRIGGER_SUBJECT_ENABLE && subject != CONTEXT_TRIGGER_SUBJECT_DISABLE &&
+                       subject != CONTEXT_TRIGGER_SUBJECT_GET && subject != CONTEXT_TRIGGER_SUBJECT_GET_RULE_IDS) {
+               return false;
+       }
+
+       push_thread_event(ETYPE_REQUEST, request);
+       return true;
+}
+
+void ctx::context_trigger::process_request(ctx::request_info* request)
+{
+       // Process the request, and reply to the client if necessary.
+       const char* req_sbj = request->get_subject();
+       _D("Request is %s", req_sbj);
+       std::string subject(req_sbj);
+
+       if (subject == CONTEXT_TRIGGER_SUBJECT_ADD) {
+               add_rule(request);
+       } else if (subject == CONTEXT_TRIGGER_SUBJECT_REMOVE) {
+               remove_rule(request);
+       } else if (subject == CONTEXT_TRIGGER_SUBJECT_ENABLE) {
+               enable_rule(request);
+       } else if (subject == CONTEXT_TRIGGER_SUBJECT_DISABLE) {
+               disable_rule(request);
+       } else if (subject == CONTEXT_TRIGGER_SUBJECT_GET) {
+               get_rule_by_id(request);
+       } else if (subject == CONTEXT_TRIGGER_SUBJECT_GET_RULE_IDS) {
+               get_rule_ids(request);
+       } else {
+               _E("Invalid request");
+       }
+}
+
+void ctx::context_trigger::push_fact(int req_id, int error, const char* subject, ctx::json& option, ctx::json& data, const char* zone)
+{
+       context_fact *fact = new(std::nothrow) context_fact(req_id, error, subject, option, data, zone);
+       IF_FAIL_VOID_TAG(fact, _E, "Memory allocation failed");
+
+       push_thread_event(ETYPE_FACT, fact);
+}
+
+void ctx::context_trigger::process_fact(ctx::context_fact* fact)
+{
+       // Process the context fact.
+       rule_mgr->on_event_received(fact->get_subject(), fact->get_option(), fact->get_data(), fact->get_zone_name());
+}
+
+void ctx::context_trigger::process_initialize(void)
+{
+       rule_mgr = new(std::nothrow) rule_manager();
+       IF_FAIL_VOID_TAG(rule_mgr, _E, "Memory allocation failed");
+
+       bool ret = rule_mgr->init(this, _reader);
+       if (!ret) {
+               _E("Context trigger initialization failed.");
+               raise(SIGTERM);
+       }
+}
+
+void ctx::context_trigger::add_rule(ctx::request_info* request)
+{
+       ctx::json rule_id;
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       const char* zone_name = request->get_zone_name();
+       if (zone_name == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       int error = rule_mgr->add_rule(app_id, request->get_description(), zone_name, &rule_id);
+       _I("'%s' adds a rule (Error: %#x)", request->get_client(), error);
+
+       request->reply(error, rule_id);
+}
+
+void ctx::context_trigger::remove_rule(ctx::request_info* request)
+{
+       int id;
+       int error;
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       ctx::json rule_id = request->get_description();
+       rule_id.get(NULL, CT_RULE_ID, &id);
+
+       error = rule_mgr->check_rule(app_id, id);
+       if (error != ERR_NONE) {
+               request->reply(error);
+               return;
+       }
+
+       bool ret = rule_mgr->is_rule_enabled(id);
+       if (ret) {
+               request->reply(ERR_RULE_ENABLED);
+               return;
+       }
+
+       error = rule_mgr->remove_rule(id);
+       _I("'%s' removes rule%d (Error: %#x)", request->get_client(), id, error);
+       request->reply(error);
+}
+
+void ctx::context_trigger::enable_rule(ctx::request_info* request)
+{
+       int id;
+       int error;
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       ctx::json rule_id = request->get_description();
+       rule_id.get(NULL, CT_RULE_ID, &id);
+
+       error = rule_mgr->check_rule(app_id, id);
+       if (error != ERR_NONE) {
+               request->reply(error);
+               return;
+       }
+
+       bool ret = rule_mgr->is_rule_enabled(id);
+       if (ret) {
+               request->reply(ERR_RULE_ENABLED);
+               return;
+       }
+
+       error = rule_mgr->enable_rule(id);
+       _I("'%s' enables rule%d (Error: %#x)", request->get_client(), id, error);
+       request->reply(error);
+}
+
+void ctx::context_trigger::disable_rule(ctx::request_info* request)
+{
+       int id;
+       int error;
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       ctx::json rule_id = request->get_description();
+       rule_id.get(NULL, CT_RULE_ID, &id);
+
+       error = rule_mgr->check_rule(app_id, id);
+       if (error != ERR_NONE) {
+               request->reply(error);
+               return;
+       }
+
+       bool ret = rule_mgr->is_rule_enabled(id);
+       if (!ret) {
+               request->reply(ERR_RULE_NOT_ENABLED);
+               return;
+       }
+
+       error = rule_mgr->disable_rule(id);
+       _I("'%s' disables rule%d (Error: %#x)", request->get_client(), id, error);
+       request->reply(error);
+}
+
+void ctx::context_trigger::get_rule_by_id(ctx::request_info* request)
+{
+       int error;
+
+       ctx::json option = request->get_description();
+       int id;
+       option.get(NULL, CT_RULE_ID, &id);
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       ctx::json read_data;
+       error = rule_mgr->get_rule_by_id(app_id, id, &read_data);
+
+       ctx::json dummy;
+       request->reply(error, dummy, read_data);
+}
+
+void ctx::context_trigger::get_rule_ids(ctx::request_info* request)
+{
+       int error;
+
+       const char* app_id = request->get_app_id();
+       if (app_id == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       ctx::json read_data;
+       error = rule_mgr->get_rule_ids(app_id, &read_data);
+
+       ctx::json dummy;
+       request->reply(error, dummy, read_data);
+}
diff --git a/src/context_trigger/trigger.h b/src/context_trigger/trigger.h
new file mode 100644 (file)
index 0000000..eda318a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_CONTEXT_TRIGGER_H__
+#define __CONTEXT_CONTEXT_TRIGGER_H__
+
+#include <event_driven.h>
+#include "../request.h"
+#include "fact.h"
+
+namespace ctx {
+
+       class fact_reader;
+       class rule_manager;
+       class client_request;
+       class context_manager_impl;
+       class context_trigger : public event_driven_thread {
+               public:
+                       context_trigger();
+                       ~context_trigger();
+
+                       bool init(ctx::context_manager_impl* mgr);
+                       void release();
+
+                       bool assign_request(ctx::request_info* request);
+                       void push_fact(int req_id, int error, const char* subject, ctx::json& option, ctx::json& data, const char* zone);
+                       bool publish(int rule_id, int error, ctx::json data);
+                       bool _publish(int rule_id, int error, ctx::json data);
+
+
+               private:
+                       enum event_type_e {
+                               ETYPE_REQUEST = 1,      // A request received from a client
+                               ETYPE_FACT,                     // A context fact received from a CA
+                               ETYPE_INITIALIZE,       // Initialization
+                       };
+
+                       ctx::fact_reader *_reader;
+
+                       void on_thread_event_popped(int type, void* data);
+                       void delete_thread_event(int type, void* data);
+
+                       void process_request(ctx::request_info* request);
+                       void process_fact(ctx::context_fact* fact);
+                       void process_initialize(void);
+
+                       void add_rule(ctx::request_info* request);
+                       void remove_rule(ctx::request_info* request);
+                       void enable_rule(ctx::request_info* request);
+                       void disable_rule(ctx::request_info* request);
+                       void get_rule_by_id(ctx::request_info* request);
+                       void get_rule_ids(ctx::request_info* request);
+
+                       ctx::rule_manager* rule_mgr;
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_CONTEXT_TRIGGER_H__ */
diff --git a/src/db_mgr_impl.cpp b/src/db_mgr_impl.cpp
new file mode 100644 (file)
index 0000000..9facb0e
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <string>
+#include <sstream>
+#include <list>
+#include <scope_mutex.h>
+#include "server.h"
+#include "db_mgr_impl.h"
+
+//TODO: conf file for this kind of env. params.
+#define CONTEXT_DB_PATH "/opt/dbspace/.context-service.db"
+
+static bool initialized = false;
+static GThread *mainthread = NULL;
+static GMutex sync_insert_mutex;
+static GMutex sync_execute_mutex;
+static GCond sync_insert_cond;
+static GCond sync_execute_cond;
+static int64_t sync_insert_row_id = -1;
+static unsigned int sync_insert_qid = 0;
+static int sync_execute_error;
+static std::vector<ctx::json> sync_execute_result;
+static unsigned int sync_execute_qid = 0;
+
+ctx::db_manager_impl::db_manager_impl()
+       : db_handle(NULL)
+{
+}
+
+ctx::db_manager_impl::~db_manager_impl()
+{
+       release();
+}
+
+bool ctx::db_manager_impl::init()
+{
+       IF_FAIL_RETURN_TAG(!initialized, true, _W, "Re-initialization");
+       IF_FAIL_RETURN(open(), false);
+       mainthread = g_thread_self();
+       IF_FAIL_RETURN(start(), false);
+       initialized = true;
+       return true;
+}
+
+void ctx::db_manager_impl::release()
+{
+       IF_FAIL_VOID(initialized);
+       initialized = false;
+       stop();
+       close();
+}
+
+bool ctx::db_manager_impl::open()
+{
+       sqlite3 *db = NULL;
+       int r = sqlite3_open(CONTEXT_DB_PATH, &db);
+
+       IF_FAIL_RETURN_TAG(r == SQLITE_OK, false, _E, "DB Error: %s", sqlite3_errmsg(db));
+
+       db_handle = db;
+       return true;
+}
+
+void ctx::db_manager_impl::close()
+{
+       if (db_handle) {
+               sqlite3_close(db_handle);
+       }
+       db_handle = NULL;
+}
+
+bool ctx::db_manager_impl::is_main_thread()
+{
+       return mainthread == g_thread_self();
+}
+
+void ctx::db_manager_impl::on_thread_event_popped(int type, void* data)
+{
+       IF_FAIL_VOID(data);
+       query_info_s *info = static_cast<query_info_s*>(data);
+
+       switch (type) {
+               case QTYPE_CREATE_TABLE:
+               case QTYPE_INSERT:
+               case QTYPE_EXECUTE:
+                       _execute(type, info->id, info->query.c_str(), info->listener);
+                       break;
+               default:
+                       _W("Unknown type: %d", type);
+                       break;
+       }
+
+       delete_thread_event(type, data);
+}
+
+void ctx::db_manager_impl::delete_thread_event(int type, void* data)
+{
+       IF_FAIL_VOID(data);
+       query_info_s *info = static_cast<query_info_s*>(data);
+       delete info;
+}
+
+bool ctx::db_manager_impl::create_table(unsigned int query_id, const char* table_name, const char* columns, const char* option, db_listener_iface* listener)
+{
+       IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized");
+
+       query_info_s *info = new(std::nothrow) query_info_s;
+       IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed");
+
+       info->query = "CREATE TABLE IF NOT EXISTS ";
+       info->query = info->query + table_name + " (row_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + columns + ")";
+       if (option) {
+               info->query = info->query + " " + option;
+       }
+       info->query += ";";
+
+       info->id = query_id;
+       info->listener = listener;
+
+       if (!push_thread_event(QTYPE_CREATE_TABLE, info)) {
+               _E("Pushing thread event failed");
+               delete info;
+               return false;
+       }
+
+       return true;
+}
+
+bool ctx::db_manager_impl::insert(unsigned int query_id, const char* table_name, json& record, db_listener_iface* listener)
+{
+       IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized");
+
+       std::list<std::string> keys;
+       IF_FAIL_RETURN_TAG(record.get_keys(&keys), false, _E, "Invalid record");
+
+       std::ostringstream colstream;
+       std::ostringstream valstream;
+
+
+       for (std::list<std::string>::iterator it = keys.begin(); it != keys.end(); ++it) {
+               std::string s;
+               int64_t i;
+               if (record.get(NULL, (*it).c_str(), &s)) {
+                       colstream << *it << ",";
+                       valstream << "'" << s << "',";
+               } else if (record.get(NULL, (*it).c_str(), &i)) {
+                       colstream << *it << ",";
+                       valstream << i << ",";
+               }
+       }
+
+       std::string cols = colstream.str();
+       std::string vals = valstream.str();
+
+       IF_FAIL_RETURN_TAG(!cols.empty(), false, _E, "Invalid record");
+
+       cols.erase(cols.size() - 1);
+       vals.erase(vals.size() - 1);
+
+       _D("Columns: %s", cols.c_str());
+       _D("Values : %s", vals.c_str());
+
+       query_info_s *info = new(std::nothrow) query_info_s;
+       IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed");
+
+       info->id = query_id;
+       info->listener = listener;
+
+       info->query = "INSERT INTO ";
+       info->query = info->query + table_name + " (" + cols + ") VALUES (" + vals + ");";
+       info->query = info->query + "SELECT seq FROM sqlite_sequence WHERE name='" + table_name + "';";
+
+       if (!push_thread_event(QTYPE_INSERT, info)) {
+               _E("Pushing thread event failed");
+               delete info;
+               return false;
+       }
+
+       return true;
+}
+
+bool ctx::db_manager_impl::execute(unsigned int query_id, const char* query, db_listener_iface* listener)
+{
+       IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized");
+
+       query_info_s *info = new(std::nothrow) query_info_s;
+       IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed");
+
+       info->id = query_id;
+       info->query = query;
+       info->listener = listener;
+
+       if (!push_thread_event(QTYPE_EXECUTE, info)) {
+               _E("Pushing thread event failed");
+               delete info;
+               return false;
+       }
+
+       return true;
+}
+
+void ctx::db_manager_impl::_execute(int query_type, unsigned int query_id, const char* query, db_listener_iface* listener)
+{
+       _SD("SQL(%d): %s", query_id, query);
+       IF_FAIL_VOID(query);
+       IF_FAIL_VOID_TAG(db_handle, _E, "DB not opened");
+
+       std::vector<json> *query_result = new(std::nothrow) std::vector<json>;
+       IF_FAIL_VOID_TAG(query_result, _E, "Memory allocation failed");
+
+       char *err = NULL;
+
+       int ret = sqlite3_exec(db_handle, query, execution_result_cb, query_result, &err);
+       if (ret != SQLITE_OK) {
+               _E("DB Error: %s", err);
+               sqlite3_free(err);
+               send_result(query_type, query_id, listener, ERR_OPERATION_FAILED, query_result);
+               return;
+       }
+
+       send_result(query_type, query_id, listener, ERR_NONE, query_result);
+       return;
+}
+
+int ctx::db_manager_impl::execution_result_cb(void *user_data, int dim, char **value, char **column)
+{
+       std::vector<json> *records = static_cast<std::vector<json>*>(user_data);
+       json row;
+       bool column_null = false;
+
+       for (int i=0; i<dim; ++i) {
+               _D("[%d] '%s': '%s'", i, column[i], value[i]);
+
+               if (value[i]) {
+                       row.set(NULL, column[i], value[i]);
+               } else {
+                       column_null = true;
+               }
+       }
+
+       if (!column_null) {
+               records->push_back(row);
+       } else {
+               _W(RED("Null columns exist"));
+       }
+
+       return 0;
+}
+
+void ctx::db_manager_impl::send_result(int query_type, unsigned int query_id, db_listener_iface* listener, int error, std::vector<json>* result)
+{
+       query_result_s *qr = new(std::nothrow) query_result_s();
+       IF_FAIL_VOID_TAG(qr, _E, "Memory allocation failed");
+
+       qr->type = query_type;
+       qr->id = query_id;
+       qr->listener = listener;
+       qr->error = error;
+       qr->result = result;
+
+       g_idle_add(_send_result, qr);
+}
+
+gboolean ctx::db_manager_impl::_send_result(gpointer data)
+{
+       query_result_s *qr = static_cast<query_result_s*>(data);
+
+       if (qr->listener) {
+               switch (qr->type) {
+                       case QTYPE_CREATE_TABLE:
+                               qr->listener->on_creation_result_received(qr->id, qr->error);
+                               break;
+                       case QTYPE_INSERT:
+                               {
+                                       int64_t row_id = -1;
+                                       if (qr->error == ERR_NONE && qr->result && !qr->result->empty()) {
+                                               qr->result->at(0).get(NULL, "seq", &row_id);
+                                               _D("RowId: %d", row_id);
+                                       }
+                                       qr->listener->on_insertion_result_received(qr->id, qr->error, row_id);
+                               }
+                               break;
+                       case QTYPE_EXECUTE:
+                               qr->listener->on_query_result_received(qr->id, qr->error, *(qr->result));
+                               break;
+                       default:
+                               _W("Unknown query type: %d", qr->type);
+               }
+       }
+
+       delete qr->result;
+       delete qr;
+       return FALSE;
+}
+
+static unsigned int generate_qid()
+{
+       static GMutex mutex;
+       static unsigned int qid = 0;
+
+       ctx::scope_mutex sm(&mutex);
+
+       ++qid;
+
+       if (qid == 0) { // Overflow handling
+               qid = 1;
+       }
+
+       return qid;
+}
+
+bool ctx::db_manager_impl::insert_sync(const char* table_name, json& record, int64_t* row_id)
+{
+       IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized");
+       IF_FAIL_RETURN_TAG(!db_manager_impl::is_main_thread(), false, _E, "Cannot use this in the main thread");
+       IF_FAIL_RETURN_TAG(table_name && row_id, false, _E, "Invalid parameter");
+
+       ctx::scope_mutex sm(&sync_insert_mutex);
+
+       unsigned int qid = generate_qid();
+       insert(qid, table_name, record, this);
+
+       while (sync_insert_qid != qid) {
+               g_cond_wait(&sync_insert_cond, &sync_insert_mutex);
+       }
+
+       *row_id = sync_insert_row_id;
+
+       IF_FAIL_RETURN_TAG(*row_id > 0, false, _E, "Insertion failed");
+       return true;
+}
+
+void ctx::db_manager_impl::on_insertion_result_received(unsigned int query_id, int error, int64_t row_id)
+{
+       ctx::scope_mutex sm(&sync_insert_mutex);
+
+       sync_insert_row_id = row_id;
+       sync_insert_qid = query_id;
+
+       g_cond_signal(&sync_insert_cond);
+}
+
+bool ctx::db_manager_impl::execute_sync(const char* query, std::vector<json>* records)
+{
+       IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized");
+       IF_FAIL_RETURN_TAG(!db_manager_impl::is_main_thread(), false, _E, "Cannot use this in the main thread");
+       IF_FAIL_RETURN_TAG(query && records, false, _E, "Invalid parameter");
+
+       ctx::scope_mutex sm(&sync_execute_mutex);
+
+       unsigned int qid = generate_qid();
+       execute(qid, query, this);
+
+       while (sync_execute_qid != qid) {
+               g_cond_wait(&sync_execute_cond, &sync_execute_mutex);
+       }
+
+       IF_FAIL_RETURN_TAG(sync_execute_error == ERR_NONE, false, _E, "Query execution failed");
+
+       *records = sync_execute_result;
+       sync_execute_result.clear();
+
+       return true;
+}
+
+void ctx::db_manager_impl::on_query_result_received(unsigned int query_id, int error, std::vector<json>& records)
+{
+       ctx::scope_mutex sm(&sync_execute_mutex);
+
+       sync_execute_error = error;
+       sync_execute_result = records;
+       sync_execute_qid = query_id;
+
+       g_cond_signal(&sync_execute_cond);
+}
+
+void ctx::db_manager_impl::on_creation_result_received(unsigned int query_id, int error)
+{
+       _D("Table Created: QID: %d, Error: %#x", query_id, error);
+}
diff --git a/src/db_mgr_impl.h b/src/db_mgr_impl.h
new file mode 100644 (file)
index 0000000..97baaa0
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_DB_MANAGER_IMPL_H__
+#define __CONTEXT_DB_MANAGER_IMPL_H__
+
+#include <vector>
+#include <json.h>
+#include <sqlite3.h>
+
+#include <event_driven.h>
+#include <db_listener_iface.h>
+#include <db_mgr_iface.h>
+
+namespace ctx {
+
+       class db_manager_impl : public event_driven_thread, public db_listener_iface, public db_manager_iface {
+               private:
+                       enum query_type_e {
+                               QTYPE_CREATE_TABLE = 1,
+                               QTYPE_INSERT,
+                               QTYPE_EXECUTE,
+                       };
+
+                       struct query_info_s {
+                               unsigned int id;
+                               db_listener_iface* listener;
+                               std::string query;
+                       };
+
+                       struct query_result_s {
+                               int type;
+                               unsigned int id;
+                               int error;
+                               db_listener_iface* listener;
+                               std::vector<json>* result;
+                       };
+
+                       sqlite3 *db_handle;
+
+                       static bool is_main_thread();
+
+                       void on_thread_event_popped(int type, void* data);
+                       void delete_thread_event(int type, void* data);
+
+                       bool open();
+                       void close();
+
+                       void _execute(int query_type, unsigned int query_id, const char* query, db_listener_iface* listener);
+                       void send_result(int query_type, unsigned int query_id, db_listener_iface* listener, int error, std::vector<json>* result);
+
+                       static int execution_result_cb(void *user_data, int dim, char **value, char **column);
+                       static gboolean _send_result(gpointer data);
+
+                       void on_creation_result_received(unsigned int query_id, int error);
+                       void on_insertion_result_received(unsigned int query_id, int error, int64_t row_id);
+                       void on_query_result_received(unsigned int query_id, int error, std::vector<json>& records);
+
+               public:
+                       db_manager_impl();
+                       ~db_manager_impl();
+
+                       bool init();
+                       void release();
+
+                       bool create_table(unsigned int query_id, const char* table_name, const char* columns, const char* option = NULL, db_listener_iface* listener = NULL);
+                       bool insert(unsigned int query_id, const char* table_name, json& record, db_listener_iface* listener = NULL);
+                       bool execute(unsigned int query_id, const char* query, db_listener_iface* listener);
+                       bool insert_sync(const char* table_name, json& record, int64_t* row_id);
+                       bool execute_sync(const char* query, std::vector<json>* records);
+
+       };      /* class db_manager */
+}
+
+#endif /* __CONTEXT_DB_MANAGER_IMPL_H__ */
diff --git a/src/dbus_server_impl.cpp b/src/dbus_server_impl.cpp
new file mode 100644 (file)
index 0000000..1834df7
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <types_internal.h>
+#include <dbus_listener_iface.h>
+#include "server.h"
+#include "client_request.h"
+#include "dbus_server_impl.h"
+
+static ctx::dbus_server_impl *_instance = NULL;
+static GDBusConnection *dbus_connection = NULL;
+static guint dbus_owner_id = 0;
+static GDBusNodeInfo *dbus_node_info = NULL;
+
+static const gchar introspection_xml[] =
+       "<node>"
+       "       <interface name='" DBUS_IFACE "'>"
+       "               <method name='" METHOD_REQUEST "'>"
+       "                       <arg type='i' name='" ARG_REQTYPE "' direction='in'/>"
+       "                       <arg type='s' name='" ARG_COOKIE "' direction='in'/>"
+       "                       <arg type='i' name='" ARG_REQID "' direction='in'/>"
+       "                       <arg type='s' name='" ARG_SUBJECT "' direction='in'/>"
+       "                       <arg type='s' name='" ARG_INPUT "' direction='in'/>"
+       "                       <arg type='i' name='" ARG_RESULT_ERR "' direction='out'/>"
+       "                       <arg type='s' name='" ARG_RESULT_ADD "' direction='out'/>"
+       "                       <arg type='s' name='" ARG_OUTPUT "' direction='out'/>"
+       "               </method>"
+       "       </interface>"
+       "</node>";
+
+static const char* req_type_to_str(int req_type)
+{
+       switch (req_type) {
+               case REQ_SUBSCRIBE:
+                       return "Subscribe";
+               case REQ_UNSUBSCRIBE:
+                       return "Unsubscribe";
+               case REQ_READ:
+                       return "Read";
+               case REQ_READ_SYNC:
+                       return "Read (Sync)";
+               case REQ_WRITE:
+                       return "Write";
+               default:
+                       return NULL;
+       }
+}
+
+static void handle_request(const char *sender, GVariant *param, GDBusMethodInvocation *invocation)
+{
+       gint req_type = 0;
+       const gchar *cookie = NULL;
+       gint req_id = 0;
+       const gchar *subject = NULL;
+       const gchar *input = NULL;
+
+       g_variant_get(param, "(i&si&s&s)", &req_type, &cookie, &req_id, &subject, &input);
+       IF_FAIL_VOID_TAG(req_type > 0 && req_id > 0 && cookie && subject && input, _E, "Invalid request");
+
+       _SD("Cookie: %s", cookie);
+       _I("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject);
+       _SI("Input: %s", input);
+
+       //TODO: Parameter validation
+
+       ctx::client_request *request = NULL;
+       try {
+               request = new ctx::client_request(req_type, sender, req_id, subject, input, cookie, invocation);
+       } catch (std::bad_alloc& ba) {
+               _E("Memory allocation failed");
+               g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
+               return;
+       } catch (int e) {
+               _E("Caught %d", e);
+               g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
+               return;
+       }
+
+       ctx::server::send_request(request);
+}
+
+static void handle_method_call(GDBusConnection *conn, const gchar *sender,
+               const gchar *obj_path, const gchar *iface, const gchar *method_name,
+               GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
+{
+       IF_FAIL_VOID_TAG(STR_EQ(obj_path, DBUS_PATH), _W, "Invalid path: %s", obj_path);
+       IF_FAIL_VOID_TAG(STR_EQ(iface, DBUS_IFACE), _W, "Invalid interface: %s", obj_path);
+
+       if (STR_EQ(method_name, METHOD_REQUEST)) {
+               handle_request(sender, param, invocation);
+       } else {
+               _W("Invalid method: %s", method_name);
+       }
+}
+
+static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
+{
+       GDBusInterfaceVTable vtable;
+       vtable.method_call = handle_method_call;
+       vtable.get_property = NULL;
+       vtable.set_property = NULL;
+
+       guint reg_id = g_dbus_connection_register_object(conn, DBUS_PATH,
+                       dbus_node_info->interfaces[0], &vtable, NULL, NULL, NULL);
+
+       if (reg_id <= 0) {
+               _E("Failed to acquire dbus");
+               raise(SIGTERM);
+       }
+
+       dbus_connection = conn;
+}
+
+static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
+{
+       _SI("Dbus name acquired: %s", name);
+}
+
+static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
+{
+       _E("Dbus name lost");
+       raise(SIGTERM);
+}
+
+ctx::dbus_server_impl::dbus_server_impl()
+{
+}
+
+ctx::dbus_server_impl::~dbus_server_impl()
+{
+       release();
+}
+
+bool ctx::dbus_server_impl::init()
+{
+       IF_FAIL_RETURN_TAG(dbus_node_info == NULL, false, _E, "Re-initialization");
+
+       dbus_node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+       IF_FAIL_RETURN_TAG(dbus_node_info != NULL, false, _E, "Initialization failed");
+
+       dbus_owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, DBUS_DEST, G_BUS_NAME_OWNER_FLAGS_NONE,
+                       on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL);
+
+       _instance = this;
+       return true;
+}
+
+void ctx::dbus_server_impl::release()
+{
+       if (dbus_connection) {
+               g_dbus_connection_flush_sync(dbus_connection, NULL, NULL);
+       }
+
+       if (dbus_owner_id > 0) {
+               g_bus_unown_name(dbus_owner_id);
+               dbus_owner_id = 0;
+       }
+
+       if (dbus_connection) {
+               g_dbus_connection_close_sync(dbus_connection, NULL, NULL);
+               g_object_unref(dbus_connection);
+               dbus_connection = NULL;
+       }
+
+       if (dbus_node_info) {
+               g_dbus_node_info_unref(dbus_node_info);
+               dbus_node_info = NULL;
+       }
+}
+
+void ctx::dbus_server_impl::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
+{
+       IF_FAIL_VOID_TAG(dest && subject && data, _E, "Parameter null");
+
+       _SI("Publish: %s, %d, %s, %#x, %s", dest, req_id, subject, error, data);
+
+       GVariant *param = g_variant_new("(isis)", req_id, subject, error, data);
+       IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed");
+
+       GError *err = NULL;
+       g_dbus_connection_call(dbus_connection, dest, DBUS_PATH, DBUS_IFACE,
+                       METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err);
+       HANDLE_GERROR(err);
+}
+
+static void handle_signal_received(GDBusConnection *conn, const gchar *sender,
+               const gchar *obj_path, const gchar *iface, const gchar *signal_name,
+               GVariant *param, gpointer user_data)
+{
+       IF_FAIL_VOID_TAG(user_data, _W, "user_data cannot be null");
+       ctx::dbus_listener_iface *listener = static_cast<ctx::dbus_listener_iface*>(user_data);
+       listener->on_signal_received(sender, obj_path, iface, signal_name, param);
+}
+
+int64_t ctx::dbus_server_impl::signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, ctx::dbus_listener_iface* listener)
+{
+       IF_FAIL_RETURN_TAG(dbus_connection, -1, _E, "Dbus not connected");
+       guint sid = g_dbus_connection_signal_subscribe(dbus_connection,
+                       sender, iface, name, path, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+                       handle_signal_received, listener, NULL);
+       return static_cast<int64_t>(sid);
+}
+
+void ctx::dbus_server_impl::signal_unsubscribe(int64_t subscription_id)
+{
+       IF_FAIL_VOID_TAG(dbus_connection, _E, "Dbus not connected");
+       IF_FAIL_VOID_TAG(subscription_id >= 0, _W, "Invalid parameter");
+       g_dbus_connection_signal_unsubscribe(dbus_connection, static_cast<guint>(subscription_id));
+}
+
+void ctx::dbus_server::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
+{
+       _instance->publish(dest, req_id, subject, error, data);
+}
diff --git a/src/dbus_server_impl.h b/src/dbus_server_impl.h
new file mode 100644 (file)
index 0000000..4980d1d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_DBUS_SERVER_IMPL_H__
+#define __CONTEXT_DBUS_SERVER_IMPL_H__
+
+#include <sys/types.h>
+#include <string>
+#include <dbus_server_iface.h>
+
+namespace ctx {
+       class dbus_server_impl : public dbus_server_iface {
+               public:
+                       dbus_server_impl();
+                       ~dbus_server_impl();
+
+                       bool init();
+                       void release();
+
+                       void publish(const char* dest, int req_id, const char* subject, int error, const char* data);
+                       int64_t signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, dbus_listener_iface* listener);
+                       void signal_unsubscribe(int64_t subscription_id);
+
+       };      /* class ctx::dbus_server */
+
+       namespace dbus_server {
+               void publish(const char* dest, int req_id, const char* subject, int error, const char* data);
+       }
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_DBUS_SERVER_IMPL_H__ */
diff --git a/src/request.cpp b/src/request.cpp
new file mode 100644 (file)
index 0000000..29e3130
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <types_internal.h>
+#include "request.h"
+
+ctx::request_info::request_info(int type, const char* client, int req_id, const char* subj, const char* desc)
+       : _type(type)
+       , _req_id(req_id)
+       , _client(client)
+       , _subject(subj)
+       , _description(desc)
+{
+}
+
+ctx::request_info::~request_info()
+{
+}
+
+int ctx::request_info::get_type()
+{
+       return _type;
+}
+
+int ctx::request_info::get_id()
+{
+       return _req_id;
+}
+
+const char* ctx::request_info::get_zone_name()
+{
+       return _zone_name.c_str();
+}
+
+const char* ctx::request_info::get_client()
+{
+       return _client.c_str();
+}
+
+const char* ctx::request_info::get_subject()
+{
+       return _subject.c_str();
+}
+
+const char* ctx::request_info::get_app_id()
+{
+       return get_client();
+}
+
+ctx::json& ctx::request_info::get_description()
+{
+       return _description;
+}
diff --git a/src/request.h b/src/request.h
new file mode 100644 (file)
index 0000000..bf8902f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_REQUEST_INFO_H__
+#define __CONTEXT_REQUEST_INFO_H__
+
+#include <string>
+#include <json.h>
+
+namespace ctx {
+
+       class request_info {
+               public:
+                       request_info(int type, const char* client, int req_id, const char* subj, const char* desc);
+                       virtual ~request_info();
+
+                       int get_type();
+                       int get_id();
+                       const char* get_zone_name();
+                       const char* get_client();
+                       const char* get_subject();
+                       ctx::json& get_description();
+                       virtual const char* get_app_id();
+
+                       virtual bool reply(int error) = 0;
+                       virtual bool reply(int error, ctx::json& request_result) = 0;
+                       virtual bool reply(int error, ctx::json& request_result, ctx::json& data_read) = 0;
+                       virtual bool publish(int error, ctx::json& data) = 0;
+
+               protected:
+                       int _type;
+                       int _req_id;
+                       std::string _zone_name;
+                       std::string _client;
+                       std::string _subject;
+                       ctx::json _description;
+       };
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_REQUEST_INFO_H__ */
diff --git a/src/server.cpp b/src/server.cpp
new file mode 100644 (file)
index 0000000..6af2a0a
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define _USE_ECORE_MAIN_LOOP_
+
+#include <stdlib.h>
+#include <new>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <types_internal.h>
+#include "dbus_server_impl.h"
+#include "db_mgr_impl.h"
+#include "timer_mgr_impl.h"
+#include "context_mgr_impl.h"
+#include "zone_util_impl.h"
+#include "access_control/privilege.h"
+#include "context_trigger/trigger.h"
+#include "server.h"
+
+#ifdef _USE_ECORE_MAIN_LOOP_
+#include <Ecore.h>
+#else
+static GMainLoop *mainloop = NULL;
+#endif
+
+static bool started = false;
+
+static ctx::context_manager_impl *context_mgr = NULL;
+static ctx::timer_manager_impl *timer_mgr = NULL;
+static ctx::db_manager_impl *database_mgr = NULL;
+static ctx::dbus_server_impl *dbus_handle = NULL;
+static ctx::context_trigger *trigger = NULL;
+
+void ctx::server::run()
+{
+       if (started) {
+               _W("Started already");
+               return;
+       }
+
+       _I("Init MainLoop");
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_init();
+       ecore_main_loop_glib_integrate();
+#else
+       mainloop = g_main_loop_new(NULL, FALSE);
+#endif
+
+       bool result = false;
+
+       _I("Init vasum context");
+       result = ctx::zone_util::init();
+       IF_FAIL_CATCH_TAG(result, _E, "Vasum context initialization failed");
+
+       _I("Init access control configuration");
+       result = ctx::privilege_manager::init();
+       IF_FAIL_CATCH_TAG(result, _E, "Access controller initialization failed");
+
+       _I("Init Timer Manager");
+       timer_mgr = new(std::nothrow) ctx::timer_manager_impl();
+       IF_FAIL_CATCH_TAG(timer_mgr, _E, "Memory allocation failed");
+       timer_manager::set_instance(timer_mgr);
+       result = timer_mgr->init();
+       IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed");
+
+       _I("Init Database Manager");
+       database_mgr = new(std::nothrow) ctx::db_manager_impl();
+       IF_FAIL_CATCH_TAG(database_mgr, _E, "Memory allocation failed");
+       db_manager::set_instance(database_mgr);
+       result = database_mgr->init();
+       IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed");
+
+       _I("Init Context Manager");
+       context_mgr = new(std::nothrow) ctx::context_manager_impl();
+       IF_FAIL_CATCH_TAG(context_mgr, _E, "Memory allocation failed");
+       context_manager::set_instance(context_mgr);
+       result = context_mgr->init();
+       IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed");
+
+       _I("Init Context Trigger");
+       trigger = new(std::nothrow) ctx::context_trigger();
+       IF_FAIL_CATCH_TAG(trigger, _E, "Memory allocation failed");
+       result = trigger->init(context_mgr);
+       IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed");
+
+       _I("Init Dbus Connection");
+       dbus_handle = new(std::nothrow) ctx::dbus_server_impl();
+       IF_FAIL_CATCH_TAG(dbus_handle, _E, "Memory allocation failed");
+       dbus_server::set_instance(dbus_handle);
+       result = dbus_handle->init();
+       IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed");
+
+       // Start the main loop
+       started = true;
+       _I(CYAN("Context-Service Launched"));
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_main_loop_begin();
+#else
+       g_main_loop_run(mainloop);
+#endif
+
+       _I(CYAN("Terminating Context-Service"));
+
+       _I("Release Context Trigger");
+       trigger->release();
+
+       _I("Release Analyzer Manager");
+       context_mgr->release();
+
+       _I("Release Dbus Connection");
+       dbus_handle->release();
+
+       _I("Close the Database");
+       database_mgr->release();
+
+       _I("Release Timer Manager");
+       timer_mgr->release();
+
+       _I("Release Access control configuration");
+       ctx::privilege_manager::release();
+
+       _I("Release Vasum context");
+       ctx::zone_util::release();
+
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_shutdown();
+#else
+       g_main_loop_unref(mainloop);
+#endif
+
+       delete trigger;
+       delete context_mgr;
+       delete dbus_handle;
+       delete database_mgr;
+       delete timer_mgr;
+       return;
+
+CATCH:
+       _E(RED("Launching Failed"));
+       delete trigger;
+       delete context_mgr;
+       delete dbus_handle;
+       delete database_mgr;
+       delete timer_mgr;
+}
+
+void ctx::server::send_request(ctx::request_info* request)
+{
+       if (!trigger->assign_request(request)) {
+               context_mgr->assign_request(request);
+       }
+}
+
+static void signal_handler(int signo)
+{
+       _I("SIGNAL %d received", signo);
+
+       // Stop the main loop
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_main_loop_quit();
+#else
+       g_main_loop_quit(mainloop);
+#endif
+}
+
+int main(int argc, char* argv[])
+{
+       static struct sigaction signal_action;
+       signal_action.sa_handler = signal_handler;
+       sigemptyset(&signal_action.sa_mask);
+
+       sigaction(SIGINT, &signal_action, NULL);
+       sigaction(SIGHUP, &signal_action, NULL);
+       sigaction(SIGTERM, &signal_action, NULL);
+       sigaction(SIGQUIT, &signal_action, NULL);
+
+#if !defined(GLIB_VERSION_2_36)
+       g_type_init();
+#endif
+
+       ctx::server::run();
+
+       return EXIT_SUCCESS;
+}
diff --git a/src/server.h b/src/server.h
new file mode 100644 (file)
index 0000000..1807998
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_SERVER_H__
+#define __CONTEXT_SERVER_H__
+
+namespace ctx {
+
+       class request_info;
+
+       namespace server {
+
+               void run();
+               void send_request(request_info* request);
+
+       };      /* namespace ctx::server */
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_SERVER_H__ */
diff --git a/src/timer_mgr_impl.cpp b/src/timer_mgr_impl.cpp
new file mode 100644 (file)
index 0000000..5e1dbcd
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <alarm.h>
+#include <scope_mutex.h>
+#include <timer_mgr.h>
+#include "timer_mgr_impl.h"
+
+#define IDENTIFIER "contextd"
+
+struct listener_info_s {
+       int timer_id;
+       ctx::timer_listener_iface* listener;
+       void* user_data;
+};
+
+typedef std::map<alarm_id_t, listener_info_s> listener_map_t;
+static listener_map_t *listener_map = NULL;
+static GMutex listener_map_mutex;
+
+static int generate_timer_id()
+{
+       static int tid = 0;
+
+       tid ++;
+       if (tid < 0) {
+               _W("Overflow");
+               tid = 1;
+       }
+
+       return tid;
+}
+
+static int alarm_expired_cb(alarm_id_t alarm_id, void* cb_data)
+{
+       int timer_id = 0;
+       ctx::timer_listener_iface *listener = NULL;
+       void* user_data;
+
+       {
+               ctx::scope_mutex sm1(&listener_map_mutex);
+               listener_map_t::iterator mit = listener_map->find(alarm_id);
+               IF_FAIL_RETURN_TAG(mit != listener_map->end(), 0, _W, "Unknown Alarm %d", alarm_id);
+               timer_id = mit->second.timer_id;
+               listener = mit->second.listener;
+               user_data = mit->second.user_data;
+       }
+
+       _D("Timer %d expired", timer_id);
+       bool repeat = listener->on_timer_expired(timer_id, user_data);
+
+       if (!repeat) {
+               _D("Stop repeating Timer %d (Alarm %d)", timer_id, alarm_id);
+               ctx::scope_mutex sm2(&listener_map_mutex);
+               alarmmgr_remove_alarm(alarm_id);
+               listener_map->erase(alarm_id);
+       }
+
+       return 0;
+}
+
+ctx::timer_manager_impl::timer_manager_impl()
+       : initialized(false)
+{
+}
+
+ctx::timer_manager_impl::~timer_manager_impl()
+{
+       release();
+}
+
+bool ctx::timer_manager_impl::init()
+{
+       IF_FAIL_RETURN_TAG(!initialized, true, _W, "Re-initialization");
+
+       listener_map = new(std::nothrow) listener_map_t;
+       IF_FAIL_RETURN_TAG(listener_map, false, _E, "Memory allocation failed");
+
+       int result = alarmmgr_init(IDENTIFIER);
+       IF_FAIL_RETURN_TAG(result == ALARMMGR_RESULT_SUCCESS, false, _E, "Alarm manager initialization failed");
+
+       result = alarmmgr_set_cb(alarm_expired_cb, this);
+       if (result != ALARMMGR_RESULT_SUCCESS) {
+               alarmmgr_fini();
+               _E("Alarm callback registration failed");
+               return false;
+       }
+
+       alarmmgr_remove_all();
+       initialized = true;
+       return true;
+}
+
+void ctx::timer_manager_impl::release()
+{
+       if (initialized) {
+               alarmmgr_remove_all();
+               alarmmgr_fini();
+               delete listener_map;
+               listener_map = NULL;
+               initialized = false;
+       }
+}
+
+int ctx::timer_manager_impl::set_for(int interval, timer_listener_iface* listener, void* user_data)
+{
+       IF_FAIL_RETURN_TAG(interval > 0 && listener, false, _E, "Invalid parameter");
+
+       alarm_id_t alarm_id;
+       int result;
+
+#if 0
+       // Implementation for Tizen 2.3
+       time_t trigger_time;
+       time(&trigger_time);
+       // time_t is in seconds.. It is the POSIX specification.
+       trigger_time += (interval * 60);
+
+       result = alarmmgr_add_alarm(ALARM_TYPE_VOLATILE, trigger_time, interval * 60, NULL, &alarm_id);
+#endif
+
+       result = alarmmgr_add_periodic_alarm_withcb(interval, QUANTUMIZE, alarm_expired_cb, this, &alarm_id);
+       IF_FAIL_RETURN_TAG(result == ALARMMGR_RESULT_SUCCESS, ERR_OPERATION_FAILED, _E, "Alarm initialization failed");
+
+       ctx::scope_mutex sm(&listener_map_mutex);
+
+       listener_info_s info;
+       info.timer_id = generate_timer_id();
+       info.listener = listener;
+       info.user_data = user_data;
+       (*listener_map)[alarm_id] = info;
+
+       _D("Timer %d was set for %dm interval", info.timer_id, interval);
+
+       return info.timer_id;
+}
+
+int ctx::timer_manager_impl::set_at(int hour, int min, int day_of_week, timer_listener_iface* listener, void* user_data)
+{
+       IF_FAIL_RETURN_TAG(
+                       hour < 24 && hour >= 0 &&
+                       min < 60 && min >= 0 &&
+                       day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY &&
+                       listener, false, _E, "Invalid parameter");
+
+       int repeat = 0;
+       if (day_of_week & timer_manager::SUN) repeat |= ALARM_WDAY_SUNDAY;
+       if (day_of_week & timer_manager::MON) repeat |= ALARM_WDAY_MONDAY;
+       if (day_of_week & timer_manager::TUE) repeat |= ALARM_WDAY_TUESDAY;
+       if (day_of_week & timer_manager::WED) repeat |= ALARM_WDAY_WEDNESDAY;
+       if (day_of_week & timer_manager::THU) repeat |= ALARM_WDAY_THURSDAY;
+       if (day_of_week & timer_manager::FRI) repeat |= ALARM_WDAY_FRIDAY;
+       if (day_of_week & timer_manager::SAT) repeat |= ALARM_WDAY_SATURDAY;
+
+       alarm_entry_t *alarm_info = alarmmgr_create_alarm();
+       IF_FAIL_RETURN_TAG(alarm_info, ERR_OPERATION_FAILED, _E, "Memory allocation failed");
+
+       time_t current_time;
+       struct tm current_tm;
+       time(&current_time);
+       tzset();
+       localtime_r(&current_time, &current_tm);
+
+       alarm_date_t alarm_time;
+       alarm_time.year = current_tm.tm_year + 1900;
+       alarm_time.month = current_tm.tm_mon + 1;
+       alarm_time.day = current_tm.tm_mday;
+       alarm_time.hour = hour;
+       alarm_time.min = min;
+       alarm_time.sec = 0;
+
+       alarmmgr_set_time(alarm_info, alarm_time);
+       alarmmgr_set_repeat_mode(alarm_info, ALARM_REPEAT_MODE_WEEKLY, repeat);
+       alarmmgr_set_type(alarm_info, ALARM_TYPE_VOLATILE);
+
+       alarm_id_t alarm_id;
+       int ret = alarmmgr_add_alarm_with_localtime(alarm_info, NULL, &alarm_id);
+       alarmmgr_free_alarm(alarm_info);
+
+       IF_FAIL_RETURN_TAG(ret == ALARMMGR_RESULT_SUCCESS, ERR_OPERATION_FAILED, _E, "Alarm initialization failed");
+
+       ctx::scope_mutex sm(&listener_map_mutex);
+
+       listener_info_s info;
+       info.timer_id = generate_timer_id();
+       info.listener = listener;
+       info.user_data = user_data;
+
+       (*listener_map)[alarm_id] = info;
+
+       _D("Timer %d was set at %02d:%02d:00 (day of week: %#x)", info.timer_id, hour, min, day_of_week);
+
+       return info.timer_id;
+}
+
+void ctx::timer_manager_impl::remove(int timer_id)
+{
+       ctx::scope_mutex sm(&listener_map_mutex);
+
+       listener_map_t::iterator it;
+       for (it = listener_map->begin(); it != listener_map->end(); ++it) {
+               if (it->second.timer_id == timer_id) {
+                       alarmmgr_remove_alarm(it->first);
+                       listener_map->erase(it);
+                       _D("Timer %d was removed", timer_id);
+                       break;
+               }
+       }
+}
diff --git a/src/timer_mgr_impl.h b/src/timer_mgr_impl.h
new file mode 100644 (file)
index 0000000..63c9e93
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_TIMER_MANAGER_IMPL_H__
+#define __CONTEXT_TIMER_MANAGER_IMPL_H__
+
+#include <timer_listener_iface.h>
+#include <timer_mgr_iface.h>
+
+namespace ctx {
+
+       class timer_manager_impl : public timer_manager_iface {
+               private:
+                       bool initialized;
+
+               public:
+                       timer_manager_impl();
+                       ~timer_manager_impl();
+
+                       bool init();
+                       void release();
+
+                       int set_for(int interval, timer_listener_iface* listener, void* user_data);
+                       int set_at(int hour, int min, int day_of_week, timer_listener_iface* listener, void* user_data);
+                       void remove(int timer_id);
+
+       };      /* class timer_manager */
+}
+
+#endif /* __CONTEXT_TIMER_MANAGER_IMPL_H__ */
diff --git a/src/zone_util_impl.cpp b/src/zone_util_impl.cpp
new file mode 100644 (file)
index 0000000..865f466
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <vasum.h>
+#include <system_info.h>
+#include <types_internal.h>
+#include "zone_util_impl.h"
+
+#define HOST_NAME ""
+
+static bool container_enabled = false;
+static vsm_context_h _vsm_ctx = NULL;
+static ctx::zone_util_impl* _instance = NULL;
+
+const char* ctx::zone_util_impl::default_zone()
+{
+       if (container_enabled)
+               return VSM_DEFAULT_ZONE;
+
+       return HOST_NAME;
+}
+
+void* ctx::zone_util_impl::join_by_name(const char* name)
+{
+       IF_FAIL_RETURN(container_enabled, NULL);
+       IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized");
+
+       if (name == NULL) {
+               _D("NULL zone name. The default zone will be used.");
+               name = VSM_DEFAULT_ZONE;
+       }
+
+       vsm_zone_h target_zone = vsm_lookup_zone_by_name(_vsm_ctx, name);
+       IF_FAIL_RETURN_TAG(target_zone, NULL, _E, RED("Zone lookup failed"));
+
+       vsm_zone_h current_zone = vsm_lookup_zone_by_pid(_vsm_ctx, getpid());
+       IF_FAIL_RETURN_TAG(current_zone, NULL, _E, RED("Zone lookup failed"));
+       IF_FAIL_RETURN_TAG(target_zone != current_zone, NULL, _I, YELLOW("Already in the target zone %s"), name);
+
+       _I(YELLOW("Joining to '%s'"), name);
+       return vsm_join_zone(target_zone);
+}
+
+void* ctx::zone_util_impl::join_to_zone(void* zone)
+{
+       IF_FAIL_RETURN(container_enabled, NULL);
+       IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized");
+       IF_FAIL_RETURN(zone, NULL);
+       vsm_zone_h target = static_cast<vsm_zone_h>(zone);
+       _I(YELLOW("Joining to '%s'"), vsm_get_zone_name(target));
+       return vsm_join_zone(target);
+}
+
+bool ctx::zone_util::init()
+{
+       system_info_get_platform_bool("tizen.org/feature/container", &container_enabled);
+       IF_FAIL_RETURN_TAG(_instance == NULL, true, _W, "Re-initialization");
+
+       _instance = new(std::nothrow) zone_util_impl();
+       IF_FAIL_RETURN_TAG(_instance, false, _E, "Memory allocation failed");
+
+       if (container_enabled) {
+               _vsm_ctx = vsm_create_context();
+               if (!_vsm_ctx) {
+                       delete _instance;
+                       _E("Memory allocation failed");
+                       return false;
+               }
+       }
+
+       zone_util::set_instance(_instance);
+       return true;
+}
+
+void ctx::zone_util::release()
+{
+       zone_util::set_instance(NULL);
+
+       delete _instance;
+       _instance = NULL;
+
+       if (_vsm_ctx)
+               vsm_cleanup_context(_vsm_ctx);
+
+       _vsm_ctx = NULL;
+}
+
+const char* ctx::zone_util::get_name_by_pid(pid_t pid)
+{
+       IF_FAIL_RETURN(container_enabled, HOST_NAME);
+       IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized");
+       vsm_zone_h zn = vsm_lookup_zone_by_pid(_vsm_ctx, pid);
+       return vsm_get_zone_name(zn);
+}
diff --git a/src/zone_util_impl.h b/src/zone_util_impl.h
new file mode 100644 (file)
index 0000000..ce2421c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__
+#define __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__
+
+#include <sys/types.h>
+#include <zone_util.h>
+#include <zone_util_iface.h>
+
+namespace ctx {
+
+       class zone_util_impl : public zone_util_iface {
+               public:
+                       zone_util_impl() {}
+                       ~zone_util_impl() {}
+                       void* join_by_name(const char* name);
+                       void* join_to_zone(void* zone);
+                       const char* default_zone();
+       };
+
+       namespace zone_util {
+               bool init();
+               void release();
+               const char* get_name_by_pid(pid_t pid);
+       }
+
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__ */