tizen 2.4 release accepted/tizen_2.4_mobile tizen_2.4 accepted/tizen/2.4/mobile/20151029.033357 submit/tizen_2.4/20151028.064102 tizen_2.4_mobile_release
authorjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 07:39:08 +0000 (16:39 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 07:39:08 +0000 (16:39 +0900)
45 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]
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]
src/access_control/peer_creds.cpp [new file with mode: 0644]
src/access_control/peer_creds.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]

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..401131b
--- /dev/null
@@ -0,0 +1,62 @@
+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
+       libsmack
+       capi-system-info
+       capi-appfw-app-manager
+       appsvc
+       alarm-service
+       notification
+       capi-system-system-settings
+       security-server
+       clips
+       context-common
+       device-context-provider
+       statistics-context-provider
+       place-context-provider
+)
+
+IF("${MAINLOOP}" STREQUAL "ecore")
+       ADD_DEFINITIONS("-D_USE_ECORE_MAIN_LOOP_")
+       SET(DEPS
+               ${DEPS}
+               ecore
+       )
+ENDIF("${MAINLOOP}" STREQUAL "ecore")
+
+# 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..1b01074
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+Copyright (c) 2015 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) 2015 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/packaging/context-service.manifest b/packaging/context-service.manifest
new file mode 100644 (file)
index 0000000..652aff3
--- /dev/null
@@ -0,0 +1,71 @@
+<manifest>
+       <define>
+               <domain name="context-service"/>
+               <permit>
+                       <smack permit="dbus" type="rwx"/>
+                       <smack permit="msg-service" type="rwx"/>
+                       <smack permit="telephony_framework" type="rw"/>
+               </permit>
+               <request>
+                       <smack request="xorg" type="rw"/>
+                       <smack request="security-server::api-cookie-check" type="w"/>
+                       <smack request="security-server::api-app-privilege-by-name" type="rlw"/>
+                       <smack request="security-server::api-permissions" type="rlw"/>
+                       <smack request="vasum::manage" type="rw"/>
+                       <smack request="sys-assert::core" type="rwxat"/>
+                       <smack request="device::app_logging" type="rw"/>
+                       <smack request="device::sys_logging" type="rw"/>
+                       <smack request="deviced::display" type="rw"/>
+                       <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="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::message" type="rl"/>
+                       <smack request="tizen::vconf::public::r" type="rl"/>
+                       <smack request="tizen::vconf::setting::admin" type="rl"/>
+                       <smack request="tizen::vconf::volume::set" type="rl"/>
+                       <smack request="sound_server" type="rlwx"/>
+                       <smack request="media-data::db" type="rlw"/>
+                       <smack request="system::homedir" type="rwxat"/>
+                       <smack request="pkgmgr::db" type="rlx"/>
+                       <smack request="pkgmgr::info" type="rl"/>
+                       <smack request="contacts-service::svc" type="rw"/>
+                       <smack request="contacts-service::phonelog" type="rw"/>
+               </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..dabc0df
--- /dev/null
@@ -0,0 +1,113 @@
+Name:       context-service
+Summary:    Context-Service
+Version:    0.6.3
+Release:    1
+Group:      System/Service
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+Source1:       context-service.service
+
+# For active window hooking, we need to use 'ecore' mainloop instead of the 'glib' mainloop.
+%define MAINLOOP ecore
+
+BuildRequires: cmake
+BuildRequires: sed
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(libxml-2.0)
+BuildRequires: pkgconfig(sqlite3)
+BuildRequires: pkgconfig(libsmack)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(capi-appfw-app-manager)
+BuildRequires: pkgconfig(appsvc)
+BuildRequires: pkgconfig(alarm-service)
+BuildRequires: pkgconfig(notification)
+BuildRequires: pkgconfig(capi-system-system-settings)
+
+%if "%{MAINLOOP}" == "ecore"
+BuildRequires: pkgconfig(ecore)
+%endif
+
+BuildRequires: pkgconfig(security-server)
+
+BuildRequires: pkgconfig(clips)
+BuildRequires: pkgconfig(context-common)
+BuildRequires: pkgconfig(context)
+
+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
+
+%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} -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DMAINLOOP=%{MAINLOOP}
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+mkdir -p %{buildroot}%{_unitdir}
+mkdir -p %{buildroot}%{_datadir}/license
+mkdir -p %{buildroot}%{_datadir}/packages
+mkdir -p %{buildroot}/opt/usr/dbspace
+mkdir -p %{buildroot}/opt/data/context-service
+install -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}
+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/
+
+%post
+sqlite3 /opt/usr/dbspace/.context-service.db "PRAGMA journal_mode = PERSIST;"
+sqlite3 /opt/usr/dbspace/.context-service.db "CREATE TABLE IF NOT EXISTS VERSION (VERSION TEXT);"
+sqlite3 /opt/usr/dbspace/.context-service.db "INSERT OR IGNORE INTO VERSION VALUES ('%{version}');"
+chsmack -a "context-service" /opt/usr/dbspace/.context-service.db*
+chown system:system /opt/usr/dbspace/.context-service.db*
+mkdir -p %{_unitdir}/graphical.target.wants
+ln -s ../context-service.service %{_unitdir}/graphical.target.wants/
+/sbin/ldconfig
+systemctl daemon-reload
+
+%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}/license/%{name}
+%{_datadir}/packages/*.xml
diff --git a/packaging/context-service.xml b/packaging/context-service.xml
new file mode 100644 (file)
index 0000000..7e60607
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns="http://tizen.org/ns/packages"
+       package="context-service"
+       version="0.0.0"
+       api-version="2.4"
+       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>
+               <privilege>http://tizen.org/privilege/display</privilege>
+               <privilege>http://tizen.org/privilege/packagemanager.info</privilege>
+       </privileges>
+</manifest>
diff --git a/src/access_control/peer_creds.cpp b/src/access_control/peer_creds.cpp
new file mode 100644 (file)
index 0000000..420f4de
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 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 <security-server.h>
+#include <types_internal.h>
+#include "peer_creds.h"
+
+bool ctx::peer_creds::get(const char *cookie, std::string &smack_label, pid_t &pid)
+{
+       gsize size;
+       char *decoded = reinterpret_cast<char*>(g_base64_decode(cookie, &size));
+       IF_FAIL_RETURN_TAG(decoded, false, _E, "Cookie decoding failed");
+
+       pid = security_server_get_cookie_pid(decoded);
+       char *label = security_server_get_smacklabel_cookie(decoded);
+       g_free(decoded);
+       IF_FAIL_RETURN_TAG(label, false, _E, "security_server_get_smacklabel_cookie() failed");
+
+       smack_label = label;
+       g_free(label);
+
+       return (pid > 0);
+}
diff --git a/src/access_control/peer_creds.h b/src/access_control/peer_creds.h
new file mode 100644 (file)
index 0000000..1054b3a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 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_PEER_CREDENTIALS_H__
+#define __CONTEXT_PEER_CREDENTIALS_H__
+
+#include <sys/types.h>
+#include <string>
+
+namespace ctx {
+       namespace peer_creds {
+               bool get(const char *cookie, std::string &smack_label, pid_t &pid);
+       }
+}      /* namespace ctx */
+
+#endif /* End of __CONTEXT_PEER_CREDENTIALS_H__ */
diff --git a/src/access_control/privilege.cpp b/src/access_control/privilege.cpp
new file mode 100644 (file)
index 0000000..1709b4e
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 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 <sys/smack.h>
+#include <types_internal.h>
+#include "privilege.h"
+
+bool ctx::privilege_manager::is_allowed(const char *client, const char *privilege)
+{
+       IF_FAIL_RETURN(privilege, true);
+
+       std::string priv = "privilege::tizen::";
+       priv += privilege;
+
+       int ret = smack_have_access(client, priv.c_str(), "rw");
+       _SD("Client: %s, Priv: %s, Enabled: %d", client, privilege, ret);
+
+       return (ret == 1);
+}
diff --git a/src/access_control/privilege.h b/src/access_control/privilege.h
new file mode 100644 (file)
index 0000000..8f9c8c7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 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>
+
+#define PRIV_ALARM_SET "alarm.set"
+
+namespace ctx {
+       namespace privilege_manager {
+               bool is_allowed(const char *client, const char *privilege);
+       }       /* 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..8270b06
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015 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 <app_manager.h>
+#include <types_internal.h>
+#include <dbus_server.h>
+#include "dbus_server_impl.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 *sender, char *app_id, GDBusMethodInvocation *inv)
+       : request_info(type, client, req_id, subj, desc)
+       , __app_id(app_id)
+       , __sender(sender)
+       , __invocation(inv)
+{
+}
+
+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));
+
+       g_free(__app_id);
+}
+
+const char* ctx::client_request::get_app_id()
+{
+       return __app_id;
+}
+
+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(__sender.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..ed5ef6a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 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 *sender, char *app_id, GDBusMethodInvocation *inv);
+               ~client_request();
+
+               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:
+               char *__app_id;
+               std::string __sender;
+               GDBusMethodInvocation *__invocation;
+       };
+
+}      /* 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..a055da8
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2015 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 <list>
+
+#include <types_internal.h>
+#include <json.h>
+#include <provider_iface.h>
+#include "server.h"
+#include "context_mgr_impl.h"
+#include "access_control/privilege.h"
+
+/* Context Providers */
+#include <internal/device_context_provider.h>
+#include <internal/statistics_context_provider.h>
+#include <internal/place_context_provider.h>
+
+struct trigger_item_format_s {
+       std::string subject;
+       int operation;
+       ctx::json attributes;
+       ctx::json options;
+       trigger_item_format_s(std::string subj, int ops, ctx::json attr, ctx::json opt)
+               : subject(subj), operation(ops), attributes(attr), options(opt) {}
+};
+
+static std::list<trigger_item_format_s> __trigger_item_list;
+
+ctx::context_manager_impl::context_manager_impl()
+{
+}
+
+ctx::context_manager_impl::~context_manager_impl()
+{
+       release();
+}
+
+bool ctx::context_manager_impl::init()
+{
+       bool ret;
+
+       ret = init_device_context_provider();
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Initialization failed: device-context-provider");
+
+       ret = init_statistics_context_provider();
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Initialization failed: statistics-context-provider");
+
+       ret = init_place_context_provider();
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Initialization failed: place-context-provider");
+
+       return true;
+}
+
+void ctx::context_manager_impl::release()
+{
+       for (provider_map_t::iterator it = provider_map.begin(); it != provider_map.end(); ++it) {
+               it->second.destroy(it->second.data);
+       }
+       provider_map.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();
+}
+
+bool ctx::context_manager_impl::register_provider(const char *subject, ctx::context_provider_info &provider_info)
+{
+       if (provider_map.find(subject) != provider_map.end()) {
+               _E("The provider for the subject '%s' is already registered.", subject);
+               return false;
+       }
+
+       _SI("Subj: %s, Priv: %s", subject, provider_info.privilege);
+       provider_map[subject] = provider_info;
+
+       return true;
+}
+
+bool ctx::context_manager_impl::register_trigger_item(const char *subject, int operation, ctx::json attributes, ctx::json options)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+       __trigger_item_list.push_back(trigger_item_format_s(subject, operation, attributes, options));
+       return true;
+}
+
+bool ctx::context_manager_impl::pop_trigger_item(std::string &subject, int &operation, ctx::json &attributes, ctx::json &options)
+{
+       IF_FAIL_RETURN(!__trigger_item_list.empty(), false);
+
+       trigger_item_format_s format = __trigger_item_list.front();
+       __trigger_item_list.pop_front();
+
+       subject = format.subject;
+       operation = format.operation;
+       attributes = format.attributes;
+       options = format.options;
+
+       return true;
+}
+
+void ctx::context_manager_impl::assign_request(ctx::request_info* request)
+{
+       switch (request->get_type()) {
+       case REQ_SUBSCRIBE:
+               subscribe(request);
+               break;
+       case REQ_UNSUBSCRIBE:
+               unsubscribe(request);
+               break;
+       case REQ_READ:
+       case REQ_READ_SYNC:
+               read(request);
+               break;
+       case REQ_WRITE:
+               write(request);
+               break;
+       case REQ_SUPPORT:
+               is_supported(request);
+               break;
+       default:
+               _E("Invalid type of request");
+               delete request;
+       }
+}
+
+bool ctx::context_manager_impl::is_supported(const char *subject)
+{
+       provider_map_t::iterator it = provider_map.find(subject);
+       return (it != provider_map.end());
+}
+
+void ctx::context_manager_impl::is_supported(request_info *request)
+{
+       if (is_supported(request->get_subject()))
+               request->reply(ERR_NONE);
+       else
+               request->reply(ERR_NOT_SUPPORTED);
+
+       delete request;
+}
+
+bool ctx::context_manager_impl::is_allowed(const char *client, const char *subject)
+{
+       provider_map_t::iterator it = provider_map.find(subject);
+       IF_FAIL_RETURN(it != provider_map.end(), false);
+       IF_FAIL_RETURN(ctx::privilege_manager::is_allowed(client, it->second.privilege), false);
+       return true;
+}
+
+ctx::context_manager_impl::request_list_t::iterator
+ctx::context_manager_impl::find_request(request_list_t& r_list, std::string subject, json& option)
+{
+       return find_request(r_list.begin(), r_list.end(), subject, option);
+}
+
+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)
+{
+       //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()) {
+                       break;
+               }
+       }
+       return it;
+}
+
+ctx::context_provider_iface *ctx::context_manager_impl::get_provider(ctx::request_info *request)
+{
+       provider_map_t::iterator it = provider_map.find(request->get_subject());
+
+       if (it == provider_map.end()) {
+               _W("Unsupported subject");
+               request->reply(ERR_NOT_SUPPORTED);
+               delete request;
+               return NULL;
+       }
+
+       if (!STR_EQ(TRIGGER_CLIENT_NAME, request->get_client()) &&
+                       !ctx::privilege_manager::is_allowed(request->get_client(), it->second.privilege)) {
+               _W("Permission denied");
+               request->reply(ERR_PERMISSION_DENIED);
+               delete request;
+               return NULL;
+       }
+
+       return it->second.create(it->second.data);
+}
+
+void ctx::context_manager_impl::subscribe(ctx::request_info *request)
+{
+       _I(CYAN("'%s' subscribes '%s' (RID-%d)"), request->get_client(), request->get_subject(), request->get_id());
+
+       context_provider_iface *provider = get_provider(request);
+       IF_FAIL_VOID(provider);
+
+       ctx::json request_result;
+       int error = provider->subscribe(request->get_subject(), request->get_description().str(), &request_result);
+
+       _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' unsubscribes '%s' (RID-%d)"), request->get_client(), request->get_subject(), 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()) != 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
+       provider_map_t::iterator ca = provider_map.find(req_found->get_subject());
+       if (ca == 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.create(ca->second.data)->unsubscribe(req_found->get_subject(), req_found->get_description());
+       request->reply(error);
+       delete request;
+       delete req_found;
+}
+
+void ctx::context_manager_impl::read(ctx::request_info *request)
+{
+       _I(CYAN("'%s' reads '%s' (RID-%d)"), request->get_client(), request->get_subject(), request->get_id());
+
+       context_provider_iface *provider = get_provider(request);
+       IF_FAIL_VOID(provider);
+
+       ctx::json request_result;
+       int error = provider->read(request->get_subject(), request->get_description().str(), &request_result);
+
+       _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)
+{
+       _I(CYAN("'%s' writes '%s' (RID-%d)"), request->get_client(), request->get_subject(), request->get_id());
+
+       context_provider_iface *provider = get_provider(request);
+       IF_FAIL_VOID(provider);
+
+       ctx::json request_result;
+       int error = provider->write(request->get_subject(), request->get_description(), &request_result);
+
+       _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)
+{
+       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);
+
+       while (target != end) {
+               if (!(*target)->publish(error, data_updated)) {
+                       return false;
+               }
+               target = find_request(++target, end, subject, option);
+       }
+
+       return true;
+}
+
+bool ctx::context_manager_impl::_reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+
+       _I("Sending data of '%s'", subject);
+       _J("Option", option);
+       _J("Data", data_read);
+
+       request_list_t::iterator end = read_request_list.end();
+       request_list_t::iterator target = find_request(read_request_list.begin(), end, subject, option);
+       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);
+
+               delete *prev;
+               read_request_list.erase(prev);
+       }
+
+       return true;
+}
+
+struct published_data_s {
+       int type;
+       ctx::context_manager_impl *mgr;
+       std::string subject;
+       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)
+               : type(t), mgr(m), subject(s), error(e)
+       {
+               option = o.str();
+               data = d.str();
+       }
+};
+
+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);
+                       break;
+               case REQ_READ:
+                       tuple->mgr->_reply_to_read(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data);
+                       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)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+
+       published_data_s *tuple = new(std::nothrow) published_data_s(REQ_SUBSCRIBE, this, subject, option, error, data_updated);
+       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)
+{
+       IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter");
+
+       published_data_s *tuple = new(std::nothrow) published_data_s(REQ_READ, this, subject, option, error, data_read);
+       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..6418224
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 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.h>
+#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);
+               bool is_allowed(const char *client, const char *subject);
+               bool pop_trigger_item(std::string &subject, int &operation, ctx::json &attributes, ctx::json &options);
+
+               /* From the interface class */
+               bool register_provider(const char *subject, context_provider_info &provider_info);
+               bool register_trigger_item(const char *subject, int operation, ctx::json attributes, ctx::json options);
+               bool publish(const char *subject, ctx::json &option, int error, ctx::json &data_updated);
+               bool reply_to_read(const char *subject, ctx::json &option, int error, ctx::json &data_read);
+
+       private:
+               typedef std::map<std::string, context_provider_info> provider_map_t;
+
+               request_list_t subscribe_request_list;
+               request_list_t read_request_list;
+               provider_map_t provider_map;
+
+               void subscribe(request_info *request);
+               void unsubscribe(request_info *request);
+               void read(request_info *request);
+               void write(request_info *request);
+               void is_supported(request_info *request);
+
+               context_provider_iface *get_provider(request_info *request);
+
+               static gboolean thread_switcher(gpointer data);
+               bool _publish(const char *subject, ctx::json option, int error, ctx::json data_updated);
+               bool _reply_to_read(const char *subject, ctx::json option, int error, ctx::json data_read);
+
+               request_list_t::iterator find_request(request_list_t& r_list, std::string subject, json& option);
+               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);
+
+       };      /* 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..2f2b749
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2015 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::rule_manager* rm)
+{
+       rule_mgr = rm;
+       init_environment();
+       _D(YELLOW("Clips handler initialized"));
+}
+
+ctx::clips_handler::~clips_handler()
+{
+       if (env) {
+               DestroyEnvironment(env);
+               env = NULL;
+       }
+
+       _D(YELLOW("Clips handler destroyed"));
+}
+
+int ctx::clips_handler::init_environment(void)
+{
+       if (env) {
+               _D("Clips environment already created");
+               return ERR_NONE;
+       }
+
+       env = CreateEnvironment();
+       if (!env) {
+               _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(env, 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(void)
+{
+       ASSERT_NOT_NULL(env);
+
+       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..0ebb747
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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(ctx::rule_manager* rm);
+                       ~clips_handler();
+
+                       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(void);
+                       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:
+                       clips_handler();
+                       int init_environment(void);
+                       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..966c62d
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2015 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 "../access_control/privilege.h"
+#include "context_monitor.h"
+#include "fact_reader.h"
+#include "timer_types.h"
+
+static ctx::fact_reader *reader = NULL;
+
+ctx::context_monitor::context_monitor()
+{
+}
+
+ctx::context_monitor::~context_monitor()
+{
+       delete timer;
+}
+
+bool ctx::context_monitor::init(ctx::fact_reader* fr, ctx::context_trigger* tr)
+{
+       reader = fr;
+       trigger = tr;
+       timer = new(std::nothrow) trigger_timer(trigger);
+       IF_FAIL_RETURN_TAG(timer, false, _E, "Memory allocation failed");
+
+       return true;
+}
+
+int ctx::context_monitor::subscribe(int rule_id, std::string subject, ctx::json event)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0) {
+               // option is event json in case of ON_TIME
+               return subscribe_timer(event);
+       }
+
+       ctx::json eoption = NULL;
+       event.get(NULL, CT_RULE_EVENT_OPTION, &eoption);
+
+       int req_id = reader->subscribe(subject.c_str(), &eoption, 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)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0) {
+               return unsubscribe_timer(option);
+       }
+
+       _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(subject.c_str(), req_id);
+               read_req_cnt_map.erase(req_id);
+       }
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::read_time(ctx::json* result)
+{
+       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, ctx::json* result)
+{
+       bool ret;
+       if (subject.compare(TIMER_CONDITION_SUBJECT) == 0) {
+               return read_time(result);
+       }
+
+       context_fact fact;
+       ret = reader->read(subject.c_str(), &option, 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;
+}
+
+int ctx::context_monitor::subscribe_timer(ctx::json option)
+{
+       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
+       for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) {
+               timer->add(time, dow);
+       }
+
+       return ERR_NONE;
+}
+
+int ctx::context_monitor::unsubscribe_timer(ctx::json option)
+{
+       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
+       for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) {
+               timer->remove(time, dow);
+       }
+
+       return ERR_NONE;
+}
+
+bool ctx::context_monitor::is_supported(std::string subject)
+{
+       if (subject.compare(TIMER_EVENT_SUBJECT) == 0
+                       || subject.compare(TIMER_CONDITION_SUBJECT) == 0) {
+               return true;
+       }
+
+       return reader->is_supported(subject.c_str());
+}
+
+bool ctx::context_monitor::is_allowed(const char *client, const char *subject)
+{
+       if (STR_EQ(subject, TIMER_EVENT_SUBJECT))
+               return privilege_manager::is_allowed(client, PRIV_ALARM_SET);
+
+       if (STR_EQ(subject, TIMER_CONDITION_SUBJECT))
+               return true;
+
+       return reader->is_allowed(client, subject);
+}
diff --git a/src/context_trigger/context_monitor.h b/src/context_trigger/context_monitor.h
new file mode 100644 (file)
index 0000000..b34dadc
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 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);
+               int unsubscribe(int rule_id, std::string subject, ctx::json option);
+               int read(std::string subject, json option, ctx::json* result);
+               bool is_supported(std::string subject);
+               bool is_allowed(const char *client, const char *subject);
+
+       private:
+               int subscribe_timer(ctx::json option);
+               int unsubscribe_timer(ctx::json option);
+               int read_day_of_month(ctx::json* result);
+               int read_day_of_week(ctx::json* result);
+               int read_time(ctx::json* result);
+               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;
+               ctx::trigger_timer* timer;
+
+       };      /* 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..960f59d
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015 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)
+       : req_id(id)
+       , error(err)
+       , subject(s)
+       , option(o)
+       , data(d)
+{
+}
+
+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;
+}
+
+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();
+}
+
+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..9411b68
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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;
+
+               public:
+                       context_fact();
+                       context_fact(int id, int err, const char* s, ctx::json& o, ctx::json& d);
+                       ~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_data(ctx::json& d);
+
+                       int get_req_id();
+                       int get_error();
+                       const char* get_subject();
+                       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..bdf6fe6
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2015 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)
+{
+       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) {
+                       return (*it)->sid;
+               }
+       }
+
+       return -1;
+}
+
+bool ctx::fact_reader::add_sub(int sid, const char* subject, json* option)
+{
+       subscr_info_s *info = new(std::nothrow) subscr_info_s(sid, subject, option);
+       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)
+{
+       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) {
+                       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)
+{
+       return _context_mgr->is_supported(subject);
+}
+
+bool ctx::fact_reader::is_allowed(const char *client, const char *subject)
+{
+       return _context_mgr->is_allowed(client, subject);
+}
+
+bool ctx::fact_reader::get_fact_definition(std::string &subject, int &operation, ctx::json &attributes, ctx::json &options)
+{
+       return _context_mgr->pop_trigger_item(subject, operation, attributes, options);
+}
+
+int ctx::fact_reader::subscribe(const char* subject, json* option, bool wait_response)
+{
+       IF_FAIL_RETURN(subject, ERR_INVALID_PARAMETER);
+
+       ctx::scope_mutex sm(&request_mutex);
+
+       int rid = find_sub(subject, option);
+       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, 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);
+
+       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)
+{
+       IF_FAIL_VOID(subject);
+
+       ctx::scope_mutex sm(&request_mutex);
+
+       int rid = find_sub(subject, option);
+       IF_FAIL_VOID_TAG(rid > 0, _W, "Unknown subscription for %s", subject);
+
+       unsubscribe(subject, rid);
+}
+
+void ctx::fact_reader::unsubscribe(const char *subject, int subscription_id)
+{
+       fact_request *req = new(std::nothrow) fact_request(REQ_UNSUBSCRIBE, CLIENT_NAME, subscription_id, subject, 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, context_fact& fact)
+{
+       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, 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);
+       last_data_read = EMPTY_JSON_OBJECT;
+
+       return true;
+}
+
+void ctx::fact_reader::reply_result(int req_id, int error, 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* subject, json* option, json* fact)
+{
+       _trigger->push_fact(req_id, error, subject, *option, *fact);
+}
diff --git a/src/context_trigger/fact_reader.h b/src/context_trigger/fact_reader.h
new file mode 100644 (file)
index 0000000..182b836
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 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);
+               bool is_allowed(const char *client, const char *subject);
+               bool get_fact_definition(std::string &subject, int &operation, ctx::json &attributes, ctx::json &options);
+
+               int subscribe(const char *subject, json *option, bool wait_response = false);
+               void unsubscribe(const char *subject, json *option);
+               void unsubscribe(const char *subject, int subscription_id);
+               bool read(const char *subject, json *option, context_fact& fact);
+
+               void reply_result(int req_id, int error, json *request_result = NULL, json *fact = NULL);
+               void publish_fact(int req_id, int error, 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;
+                       subscr_info_s(int id, const char *subj, ctx::json *opt)
+                               : sid(id), subject(subj)
+                       {
+                               if (opt)
+                                       option = *opt;
+                       }
+               };
+
+               typedef std::list<subscr_info_s*> subscr_list_t;
+               subscr_list_t subscr_list;
+
+               int find_sub(const char *subject, json *option);
+               bool add_sub(int sid, const char *subject, json *option);
+               void remove_sub(const char *subject, json *option);
+               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..b4017bd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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, fact_reader* reader)
+       : request_info(type, client, req_id, subj, desc)
+       , _reader(reader)
+       , replied(false)
+{
+}
+
+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);
+       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, &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, &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, _subject.c_str(), &get_description(), &data);
+       return true;
+}
diff --git a/src/context_trigger/fact_request.h b/src/context_trigger/fact_request.h
new file mode 100644 (file)
index 0000000..6ad52c5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 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, fact_reader* reader);
+                       ~fact_request();
+
+                       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..c11d976
--- /dev/null
@@ -0,0 +1,1378 @@
+/*
+ * Copyright (c) 2015 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 <device/display.h>
+#include <notification.h>
+#include <notification_internal.h>
+#include <runtime_info.h>
+#include <system_settings.h>
+#include <context_trigger_types_internal.h>
+#include <context_trigger.h>
+#include <db_mgr.h>
+#include "../dbus_server_impl.h"
+#include <app_manager.h>
+#include "fact_reader.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, creator_app_id TEXT DEFAULT '' NOT NULL, description TEXT DEFAULT '', details 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 ''"
+#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 ''"
+#define CREATE_TEMPLATE_TABLE "CREATE TABLE IF NOT EXISTS context_trigger_template (name TEXT DEFAULT '' NOT NULL PRIMARY KEY, operation INTEGER DEFAULT 3 NOT NULL, attributes TEXT DEFAULT '' NOT NULL, options TEXT DEFAULT '' NOT NULL)"
+#define QUERY_TEMPLATE_TABLE "SELECT name, operation, attributes, options 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_ATTRIBUTES_BY_RULE_ID_STATEMENT "SELECT context_trigger_condition.name, instance_name, attributes FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id = "
+#define QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT "SELECT DISTINCT context_trigger_condition.name, instance_name, option, attributes, options FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id IN (SELECT row_id FROM context_trigger_rule WHERE enabled = 1 AND row_id IN (SELECT rule_id FROM context_trigger_event WHERE context_trigger_event.instance_name = '"
+#define QUERY_RULE_BY_RULE_ID "SELECT details FROM context_trigger_rule WHERE row_id = "
+#define QUERY_EVENT_TEMPLATE_BY_RULE_ID "SELECT name, attributes, options FROM context_trigger_template WHERE name IN (SELECT name FROM context_trigger_event WHERE rule_id = "
+#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 enb_rule_cnt = 0;
+
+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;
+}
+
+static bool convert_str_to_json(ctx::json* val, const char* path, const char* key)
+{
+       // TODO:
+       IF_FAIL_RETURN(val, false);
+
+       std::string buf;
+       IF_FAIL_RETURN(val->get(path, key, &buf), false);
+
+       ctx::json temp = buf;
+       IF_FAIL_RETURN(val->set(path, key, temp), false);
+
+       return true;
+}
+
+ctx::rule_manager::rule_manager()
+{
+}
+
+ctx::rule_manager::~rule_manager()
+{
+       destroy_clips();
+}
+
+bool ctx::rule_manager::init(ctx::context_trigger* tr, ctx::fact_reader* fr)
+{
+       bool ret;
+       int error;
+
+       clips_h = NULL;
+       trigger = tr;
+       ret = c_monitor.init(fr, tr);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Context monitor initialization failed");
+
+       // 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");
+
+       // Foreign keys on
+       std::vector<json> record;
+       ret = db_manager::execute_sync(FOREIGN_KEYS_ON, &record);
+       IF_FAIL_RETURN_TAG(ret, false, _E, "Foreign keys on failed");
+
+       apply_templates(fr);
+
+       if (get_uninstalled_app() > 0) {
+               error = clear_rule_of_uninstalled_app(true);
+               IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Failed to remove uninstalled apps' rules while initialization");
+       }
+       ret = reenable_rule();
+
+       return ret;
+}
+
+void ctx::rule_manager::apply_templates(ctx::fact_reader *fr)
+{
+       std::string subject;
+       int operation;
+       ctx::json attributes;
+       ctx::json options;
+       std::string q_update;
+       std::string q_insert = "INSERT OR IGNORE INTO context_trigger_template (name, operation, attributes, options) VALUES";
+
+       while (fr->get_fact_definition(subject, operation, attributes, options)) {
+               _D("Subject: %s, Ops: %d", subject.c_str(), operation);
+               _J("Attr", attributes);
+               _J("Opt", options);
+
+               q_update += "UPDATE context_trigger_template SET operation=" + int_to_string(operation)
+                       + ", attributes='" + attributes.str() + "', options='" + options.str() + "' WHERE name='" + subject + "';";
+
+               q_insert += " ('" + subject + "', " + int_to_string(operation) + ", '" + attributes.str() + "', '" + options.str() + "'),";
+       }
+
+       q_insert.erase(q_insert.end() - 1, q_insert.end());
+       q_insert += ";";
+
+       bool ret = db_manager::execute(5, q_update.c_str(), NULL);
+       if (!ret)
+               _E("Update item definition failed");
+
+       ret = db_manager::execute(6, q_insert.c_str(), NULL);
+       IF_FAIL_VOID_TAG(ret, _E, "Insert item definition failed");
+}
+
+int ctx::rule_manager::get_uninstalled_app(void)
+{
+       // Return number of uninstalled apps
+       std::string q1 = "SELECT DISTINCT creator_app_id FROM context_trigger_rule";
+
+       std::vector<json> record;
+       bool ret = db_manager::execute_sync(q1.c_str(), &record);
+       IF_FAIL_RETURN_TAG(ret, -1, _E, "Query creators of registered 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 app_id;
+               elem.get(NULL, "creator_app_id", &app_id);
+
+               if (is_uninstalled_package(app_id)) {
+                       uninstalled_apps.insert(app_id);
+               }
+       }
+
+       return uninstalled_apps.size();
+}
+
+bool ctx::rule_manager::is_uninstalled_package(std::string app_id)
+{
+       IF_FAIL_RETURN_TAG(!app_id.empty(), false, _D, "Empty app id");
+
+       app_info_h app_info;
+       int     error = app_manager_get_app_info(app_id.c_str(), &app_info);
+
+       if (error == APP_MANAGER_ERROR_NONE) {
+               app_info_destroy(app_info);
+       } else if (error == APP_MANAGER_ERROR_NO_SUCH_APP) {
+               // Uninstalled app found
+               _D("Uninstalled app found: %s", app_id.c_str());
+               return true;
+       } else {
+               _E("Get app info(%s) failed: %d", app_id.c_str(), error);
+       }
+
+       return false;
+}
+
+int ctx::rule_manager::clear_rule_of_uninstalled_app(bool is_init)
+{
+       if (uninstalled_apps.size() <= 0) {
+               return ERR_NONE;
+       }
+
+       int error;
+       bool ret;
+
+       _D("Clear uninstalled apps' rule started");
+       // creator list
+       std::string creator_list = "(";
+       std::set<std::string>::iterator it = uninstalled_apps.begin();
+       creator_list += "creator_app_id = '" + *it + "'";
+       it++;
+       for (; it != uninstalled_apps.end(); ++it) {
+               creator_list += " OR creator_app_id = '" + *it + "'";
+       }
+       creator_list += ")";
+
+       // After event received, disable all the enabled rules of uninstalled apps
+       if (!is_init) {
+               std::string q1 = "SELECT row_id, details FROM context_trigger_rule WHERE enabled = 1 and (";
+               q1 += creator_list;
+               q1 += ")";
+
+               std::vector<json> record;
+               ret = db_manager::execute_sync(q1.c_str(), &record);
+               IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query enabled rules of uninstalled apps 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;
+                       error = disable_uninstalled_rule(elem);
+                       IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to disable rules" );
+               }
+               _D("Uninstalled apps' rules are disabled");
+       }
+
+       // Delete rules of uninstalled apps from DB
+       std::string q2 = "DELETE FROM context_trigger_rule WHERE " + creator_list;
+       std::vector<json> dummy;
+       ret = db_manager::execute_sync(q2.c_str(), &dummy);
+       IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed");
+       _D("Uninstalled apps's rule are deleted from db");
+
+       uninstalled_apps.clear();
+
+       return ERR_NONE;
+}
+
+int ctx::rule_manager::disable_uninstalled_rule(ctx::json& rule_info)
+{
+       int error;
+       bool ret;
+
+       int rule_id;
+       rule_info.get(NULL, "row_id", &rule_id);
+
+       // For event with options
+       std::string r1;
+       rule_info.get(NULL, "details", &r1);
+       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);
+       IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Failed to unsubscribe %s of rule%d: %d", ename.c_str(), rule_id, error);
+
+       // 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, "Failed to undefine rule%d: %d", rule_id, error);
+
+       // Remove condition instances
+       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, "Failed to query condition table of rule%d failed: %d", rule_id, error);
+
+       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_TAG(error == ERR_NONE, error, _E, "Failed to unmake instance %s of rule%d: %d", ciname.c_str(), rule_id, error);
+
+                               cond_cnt_map.erase(ciname);
+                       }
+               }
+       }
+
+       if (--enb_rule_cnt <= 0) {
+               enb_rule_cnt = 0;
+               destroy_clips();
+       }
+       return ERR_NONE;
+}
+
+bool ctx::rule_manager::initialize_clips(void)
+{
+       if (clips_h) {
+               _D("CLIPS handler already initialized");
+               return true;
+       }
+
+       clips_h = new(std::nothrow) clips_handler(this);
+       IF_FAIL_RETURN_TAG(clips_h, false, _E, "CLIPS handler initialization failed");
+
+       // Load all templates from DB
+       std::vector<json> record;
+       bool 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 tmpl = *vec_pos;
+               convert_str_to_json(&tmpl, NULL, "attributes");
+               convert_str_to_json(&tmpl, NULL, "options");
+
+               std::string deftemplate_str = script_generator::generate_deftemplate(tmpl);
+               int 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");
+       }
+
+       _D(YELLOW("Deftemplate, Defclass, Make-instance completed"));
+       return true;
+}
+
+void ctx::rule_manager::destroy_clips(void)
+{
+       delete clips_h;
+       clips_h = NULL;
+}
+
+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 {
+                       _D("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
+       ctx::json laction, raction;
+       lrule.get(NULL, CT_RULE_ACTION, &laction);
+       rrule.get(NULL, CT_RULE_ACTION, &raction);
+       if (laction != raction)
+               return false;
+
+       return true;
+}
+
+int64_t ctx::rule_manager::get_duplicated_rule_id(std::string creator, ctx::json& rule)
+{
+       std::string q = "SELECT row_id, description, details FROM context_trigger_rule WHERE creator = '";
+       q += creator;
+       q += "'";
+
+       std::vector<json> d_record;
+       bool ret = db_manager::execute_sync(q.c_str(), &d_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::string r_desc;
+       rule.get(NULL, CT_RULE_DESCRIPTION, &r_desc);
+       std::vector<json>::iterator vec_end = d_record.end();
+
+       for (std::vector<json>::iterator vec_pos = d_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);
+
+                       // Description comparison
+                       std::string d_desc;
+                       elem.get(NULL, "description", &d_desc);
+                       if (r_desc.compare(d_desc)) {
+                               // Only description is changed
+                               std::string q_update = "UPDATE context_trigger_rule SET description='" + r_desc + "' WHERE row_id = " + int_to_string(row_id);
+
+                               std::vector<json> record;
+                               ret = db_manager::execute_sync(q_update.c_str(), &record);
+                               if (ret) {
+                                       _D("Rule%lld description is updated", row_id);
+                               } else {
+                                       _W("Failed to update description of rule%lld", row_id);
+                               }
+                       }
+
+                       return row_id;
+               }
+       }
+
+       return -1;
+}
+
+int ctx::rule_manager::verify_rule(ctx::json& rule, const char* creator)
+{
+       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), ERR_NOT_SUPPORTED, _I, "Event(%s) is not supported", e_name.c_str());
+
+       if (creator) {
+               if (!c_monitor.is_allowed(creator, 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), ERR_NOT_SUPPORTED, _I, "Condition(%s) is not supported", c_name.c_str());
+
+               if (!c_monitor.is_allowed(creator, 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, const char* app_id, ctx::json rule, 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());
+       IF_FAIL_RETURN(err==ERR_NONE, err);
+
+       // Check if duplicated rule exits
+       if ((rid = get_duplicated_rule_id(creator, rule)) > 0) {
+               // Save rule id
+               rule_id->set(NULL, CT_RULE_ID, rid);
+               _D("Duplicated rule found");
+               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);
+       if (app_id) {
+               r_record.set(NULL, "creator_app_id", app_id);
+       }
+       r_record.set(NULL, "description", description);
+       r_record.set(NULL, "details", details.str());
+       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);
+       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);
+
+               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)
+{
+       if (enb_rule_cnt == 0) {
+               IF_FAIL_RETURN_TAG(initialize_clips(), ERR_OPERATION_FAILED, _E, "Failed to init clips");
+       }
+
+       // Subscribe event
+       int error;
+       std::string query;
+       std::string ename;
+       std::string script;
+       std::string tmp;
+
+       ctx::json jrule;
+       ctx::json jetemplate;
+       ctx::json jevent;
+       ctx::json inst_names;
+
+       std::vector<json> rule_record;
+       std::vector<json> etmpl_record;
+       std::vector<json> cond_record;
+       std::vector<json> record;
+       std::vector<json>::iterator vec_end;
+
+       std::string id_str = int_to_string(rule_id);
+
+       // Get rule json by rule id;
+       query = QUERY_RULE_BY_RULE_ID;
+       query += int_to_string(rule_id);
+       error = (db_manager::execute_sync(query.c_str(), &rule_record))? ERR_NONE : ERR_OPERATION_FAILED;
+       IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query rule by rule id failed");
+
+       rule_record[0].get(NULL, "details", &tmp);
+       jrule = tmp;
+       jrule.get(NULL, CT_RULE_EVENT, &jevent);
+
+       // Get event template by rule id
+       query = QUERY_EVENT_TEMPLATE_BY_RULE_ID;
+       query += int_to_string(rule_id);
+       query += ")";
+       error = (db_manager::execute_sync(query.c_str(), &etmpl_record))? ERR_NONE : ERR_OPERATION_FAILED;
+       IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query event template by rule id failed");
+
+       jetemplate = etmpl_record[0].str();
+       convert_str_to_json(&jetemplate, NULL, "attributes");
+       convert_str_to_json(&jetemplate, NULL, "options");
+
+       // Query name, instance name & attributes for conditions of the rule
+       query = QUERY_NAME_INSTANCE_NAME_AND_ATTRIBUTES_BY_RULE_ID_STATEMENT;
+       query += id_str;
+       error = (db_manager::execute_sync(query.c_str(), &cond_record))? ERR_NONE : ERR_OPERATION_FAILED;
+       IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Query condition's names, instance names, attributes by rule id failed");
+
+       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;
+               elem.get(NULL, "name", &cname);
+               elem.get(NULL, "instance_name", &ciname);
+               convert_str_to_json(&elem, NULL, "attributes");
+
+               // 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(elem);
+                               error = (makeinst_script.length() > 0)? ERR_NONE : ERR_OPERATION_FAILED;
+                               IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Make instance script generation failed");
+                               error = clips_h->make_instance(makeinst_script);
+                               IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Add condition instance([%s]) failed", ciname.c_str());
+
+                               cond_cnt_map[ciname] = 1;
+                       } else {
+                               cond_cnt_map[ciname]++;
+                       }
+               }
+       }
+
+       // Subscribe event
+       jetemplate.get(NULL, "name", &ename);
+       error = c_monitor.subscribe(rule_id, ename, jevent);
+       IF_FAIL_CATCH(error == ERR_NONE);
+
+       // Generate defrule script and execute it
+       script = script_generator::generate_defrule(id_str, jetemplate, jrule, inst_names);
+       error = clips_h->define_rule(script);
+       IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Defrule failed");
+
+       // Update db to set 'enabled'
+       query = UPDATE_RULE_ENABLED_STATEMENT;
+       query += id_str;
+       error = (db_manager::execute_sync(query.c_str(), &record))? ERR_NONE : ERR_OPERATION_FAILED;
+       IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Update db failed");
+
+       enb_rule_cnt++;
+       _D(YELLOW("Enable Rule%d succeeded"), rule_id);
+
+       return ERR_NONE;
+
+CATCH:
+       if (enb_rule_cnt <= 0) {
+               enb_rule_cnt = 0;
+               destroy_clips();
+       }
+
+       return error;
+}
+
+std::string ctx::rule_manager::get_instance_name(std::string name, ctx::json& option)
+{
+       std::string inst_name = name;
+       std::vector<json> record_tmpl;
+       ctx::json tmpl_c;
+       std::list<std::string> option_keys;
+
+       // Get template for the option
+       std::string q = "SELECT options FROM context_trigger_template WHERE name = '";
+       q += name;
+       q += "'";
+       db_manager::execute_sync(q.c_str(), &record_tmpl);
+
+       convert_str_to_json(&record_tmpl[0], NULL, "options");
+       record_tmpl[0].get(NULL, "options", &tmpl_c);
+
+       tmpl_c.get_keys(&option_keys);
+
+       for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
+               std::string key = (*it);
+               std::string val_str;
+               int val;
+
+               if (option.get(NULL, key.c_str(), &val_str)) {
+                       inst_name += INSTANCE_NAME_DELIMITER;
+                       inst_name += val_str;
+               } else if (option.get(NULL, 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_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;
+       rule_record[0].get(NULL, "details", &r1);
+       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);
+       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
+       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);
+                       }
+               }
+       }
+
+       if (--enb_rule_cnt <= 0) {
+               enb_rule_cnt = 0;
+               destroy_clips();
+       }
+       return ERR_NONE;
+}
+
+void ctx::rule_manager::make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption)
+{
+       std::list<std::string> option_keys;
+       ctemplate.get_keys(&option_keys);
+
+       for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
+               std::string key = (*it);
+
+               std::string coption_valstr;
+               if (coption->get(NULL, 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, key.c_str(), e_valstr);
+                               } else if (edata.get(NULL, event_key.c_str(), &e_val)) {
+                                       coption->set(NULL, key.c_str(), e_val);
+                               }
+                       }
+               }
+       }
+}
+
+void ctx::rule_manager::on_event_received(std::string item, ctx::json option, ctx::json data)
+{
+       _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;
+
+       // Generate event fact script
+       std::string q1 = "SELECT attributes, options FROM context_trigger_template WHERE name = '";
+       q1 += item;
+       q1 += "'";
+       std::vector<json> etemplate_record;
+       db_manager::execute_sync(q1.c_str(), &etemplate_record);
+
+       ctx::json etemplate = etemplate_record[0];
+       convert_str_to_json(&etemplate, NULL, "attributes");
+       convert_str_to_json(&etemplate, NULL, "options");
+
+       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 += "'))";
+       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++) {
+               convert_str_to_json(&conds[i], NULL, "options");
+               convert_str_to_json(&conds[i], NULL, "attributes");
+
+               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(conds[i], data, &coption);    //TODO: conds[i] -> "options"
+               }
+
+               // TODO: Check permission of a condition(cname), if permission granted, read condition data. (or, condition data should be empty json)
+
+               //      Get Context Data
+               ctx::json condition_data;
+               err = c_monitor.read(cname, coption, &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       // TODO: conds[i] => "attributes"
+               std::string modifyinst_script = script_generator::generate_modifyinstance(ciname, conds[i], 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");
+
+       // Clear uninstalled apps' rules if triggered
+       if (uninstalled_apps.size() > 0) {
+               err = clear_rule_of_uninstalled_app();
+               IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Failed to clear uninstalled apps' rules");
+       }
+}
+
+static void trigger_action_app_control(ctx::json& action)
+{
+       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);
+
+       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);
+
+       error = device_display_change_state(DISPLAY_STATE_NORMAL);
+       if (error != DEVICE_ERROR_NONE) {
+               _E("Change display state failed(%d)", error);
+       }
+}
+
+static void trigger_action_notification(ctx::json& action, std::string app_id)
+{
+       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);
+               }
+       }
+
+       if (!app_id.empty()) {
+               error = notification_set_pkgname(notification, app_id.c_str());
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set pkgname(%s) failed(%d)", app_id.c_str(), error);
+               }
+       }
+
+       bool silent = true;
+       error = system_settings_get_value_bool(SYSTEM_SETTINGS_KEY_SOUND_SILENT_MODE, &silent);
+       if (error != SYSTEM_SETTINGS_ERROR_NONE) {
+               _E("Get system setting(silent mode) failed(%d)", error);
+       }
+
+       bool vibration = true;
+       error = runtime_info_get_value_bool(RUNTIME_INFO_KEY_VIBRATION_ENABLED, &vibration);
+       if (error != RUNTIME_INFO_ERROR_NONE) {
+               _E("Get runtime info(vibration) failed(%d)", error);
+       }
+
+       if (!silent) {
+           error = notification_set_sound(notification, NOTIFICATION_SOUND_TYPE_DEFAULT, NULL);
+               if (error != NOTIFICATION_ERROR_NONE) {
+                       _E("Set notification sound failed(%d)", error);
+               }
+
+               if (vibration) {
+                       error = notification_set_vibration(notification, NOTIFICATION_VIBRATION_TYPE_DEFAULT, NULL);
+                       if (error != NOTIFICATION_ERROR_NONE) {
+                               _E("Set notification vibration failed(%d)", error);
+                       }
+               }
+       }
+
+       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);
+       }
+
+       error = device_display_change_state(DISPLAY_STATE_NORMAL);
+       if (error != DEVICE_ERROR_NONE) {
+               _E("Change display state failed(%d)", error);
+       }
+}
+
+static void trigger_action_dbus_call(ctx::json& action)
+{
+       std::string bus_name, object, iface, method;
+       GVariant *param = NULL;
+
+       action.get(NULL, CT_RULE_ACTION_DBUS_NAME, &bus_name);
+       IF_FAIL_VOID_TAG(!bus_name.empty(), _E, "No target bus name");
+
+       action.get(NULL, CT_RULE_ACTION_DBUS_OBJECT, &object);
+       IF_FAIL_VOID_TAG(!object.empty(), _E, "No object path");
+
+       action.get(NULL, CT_RULE_ACTION_DBUS_INTERFACE, &iface);
+       IF_FAIL_VOID_TAG(!iface.empty(), _E, "No interface name");
+
+       action.get(NULL, CT_RULE_ACTION_DBUS_METHOD, &method);
+       IF_FAIL_VOID_TAG(!method.empty(), _E, "No method name");
+
+       action.get(NULL, CT_RULE_ACTION_DBUS_PARAMETER, &param);
+
+       ctx::dbus_server::call(bus_name.c_str(), object.c_str(), iface.c_str(), method.c_str(), param);
+}
+
+void ctx::rule_manager::on_rule_triggered(int rule_id)
+{
+       std::string q = "SELECT details, creator_app_id 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);
+       if (record.empty()) {
+               _E("Rule%d not exist", rule_id);
+               return;
+       }
+
+       // If rule's creator is uninstalled, skip action
+       std::string app_id;
+       record[0].get(NULL, "creator_app_id", &app_id);
+       if (is_uninstalled_package(app_id)) {
+               _D(YELLOW("Rule%d's creator(%s) is uninstalled. Skip action."), rule_id, app_id.c_str());
+               uninstalled_apps.insert(app_id);
+               return;
+       }
+
+       _D(YELLOW("Rule%d is triggered"), rule_id);
+
+       // Do action
+       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 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);
+               } else if (type.compare(CT_RULE_ACTION_TYPE_NOTIFICATION) == 0) {
+                       trigger_action_notification(action, app_id);
+               } else if (type.compare(CT_RULE_ACTION_TYPE_DBUS_CALL) == 0) {
+                       trigger_action_dbus_call(action);
+               }
+       }
+}
+
+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 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;
+       record[0].get(NULL, "description", &description);
+
+       (*request_result).set(NULL, CT_RULE_ID, rule_id);
+       (*request_result).set(NULL, CT_RULE_DESCRIPTION, description);
+
+       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..2585a53
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015 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 <set>
+#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, const char* app_id, ctx::json rule, 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);
+                       void on_rule_triggered(int rule_id);
+
+               private:
+                       clips_handler* clips_h;
+                       context_monitor c_monitor;
+
+                       void apply_templates(ctx::fact_reader *fr);
+                       bool reenable_rule(void);
+                       int verify_rule(ctx::json& rule, const char* app_id);
+                       int64_t get_duplicated_rule_id(std::string creator, 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);
+                       int get_uninstalled_app(void);
+                       bool is_uninstalled_package(std::string app_id);
+                       int clear_rule_of_uninstalled_app(bool is_init = false);
+                       int disable_uninstalled_rule(ctx::json& rule_info);
+                       bool initialize_clips(void);
+                       void destroy_clips(void);
+
+                       std::map<std::string, int> cond_cnt_map; // <condition instance name, count>
+                       std::set<std::string> uninstalled_apps;
+   };  /* 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..fd2ee72
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2015 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_tmpl, 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& tmpl)
+{
+       std::string script;
+       std::string name;
+       ctx::json attrs;
+       ctx::json options;
+       std::list<std::string> attr_keys;
+       std::list<std::string> option_keys;
+       std::set<std::string> slot;
+
+       tmpl.get(NULL, "name", &name);
+       tmpl.get(NULL, "attributes", &attrs);
+       tmpl.get(NULL, "options", &options);
+
+       attrs.get_keys(&attr_keys);
+       options.get_keys(&option_keys);
+
+       for (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               slot.insert(*it);
+       }
+
+       for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
+               slot.insert(*it);
+       }
+
+       //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 nil))";
+       }
+       script += ")\n";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_defclass(ctx::json& tmpl)
+{
+       std::string script;
+       std::string name;
+       ctx::json attrs;
+       std::list<std::string> attr_keys;
+
+       tmpl.get(NULL, "name", &name);
+       tmpl.get(NULL, "attributes", &attrs);
+
+       attrs.get_keys(&attr_keys);
+
+       //class name is "C.itemname"
+       script = "(defclass C.";
+       script += name;
+       script += " (is-a USER) (role concrete) ";
+
+       for (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               script += "(slot " + (*it) + " (default nil)(create-accessor read-write))";
+       }
+       script += ")\n";
+
+       return script;
+}
+
+std::string ctx::script_generator::generate_makeinstance(ctx::json& tmpl)
+{
+       std::string script;
+       std::string name;
+       ctx::json attrs;
+       std::list<std::string> attr_keys;
+
+       tmpl.get(NULL, "name", &name);
+       tmpl.get(NULL, "attributes", &attrs);
+
+       attrs.get_keys(&attr_keys);
+
+       std::string instance_name;
+       if (!tmpl.get(NULL, "instance_name", &instance_name)) {
+               // For default instance w/o option
+               instance_name = name;
+       }
+
+       //instance name is "[itemname]"
+       script = "([" + instance_name + "] of C." + name;
+
+       for (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               script += " (" + (*it) + " 0)";
+       }
+       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_tmpl, ctx::json& rule, ctx::json& inst_names)
+{
+       std::string script;
+       ctx::json option = NULL;
+       rule.get(CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &option);
+
+       script = "(defrule rule" + rule_id + " ";
+       script += generate_initial_fact(event_tmpl, 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 ";
+
+               if (eventdata_num > 0) {
+                       ctx::json event;
+                       rule.get(NULL, CT_RULE_EVENT, &event);
+                       script += "(and ";
+                       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);
+
+               if (eventdata_num > 0)
+                       script += ")";
+
+               script = script + " then (execute_action rule" + rule_id + "))";
+       } else if (eventdata_num > 0) {
+               // case2: no conditions, but event data
+               ctx::json event;
+               rule.get(NULL, CT_RULE_EVENT, &event);
+
+               script += "(if ";
+               script += generate_event_data(event);
+               script = script + " then (execute_action rule" + rule_id + "))";
+       } else {
+               // case3: only action
+               script = script + " (execute_action rule" + rule_id + ")";
+       }
+
+       script += ")";
+       _D("Defrule script generated: %s", script.c_str());
+       return script;
+}
+
+std::string generate_initial_fact(ctx::json& event_tmpl, ctx::json& option)
+{
+       std::string script;
+       std::string e_name;
+       ctx::json attrs;
+       ctx::json options;
+       std::list<std::string> attr_keys;
+       std::list<std::string> option_keys;
+
+       event_tmpl.get(NULL, "name", &e_name);
+       event_tmpl.get(NULL, "attributes", &attrs);
+       event_tmpl.get(NULL, "options", &options);
+
+       attrs.get_keys(&attr_keys);
+       options.get_keys(&option_keys);
+
+       script += "(" + e_name + " ";
+       // options
+       for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
+               std::string opt_key = (*it);
+               script += "(" + opt_key + " ";
+
+               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 += "?" + opt_key;
+               }
+               script += ")";
+       }
+
+       // attributes
+       for (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               std::string attr_key = (*it);
+               script += "(" + attr_key + " ?" + attr_key + ")";
+       }
+       script += ")";
+
+       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_tmpl, ctx::json& option, ctx::json& data)
+{
+       // Generate Fact script for invoked event
+       std::string script = "(" + item_name + " ";
+       ctx::json attrs;
+       ctx::json options;
+       std::list<std::string> attr_keys;
+       std::list<std::string> option_keys;
+
+       event_tmpl.get(NULL, "attributes", &attrs);
+       event_tmpl.get(NULL, "options", &options);
+
+       attrs.get_keys(&attr_keys);
+       options.get_keys(&option_keys);
+
+       for (std::list<std::string>::iterator it = option_keys.begin(); it != option_keys.end(); ++it) {
+               std::string opt_key = (*it);
+               std::string val_str;
+               int value;
+
+               script += "(" + opt_key + " ";
+
+               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 (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               std::string attr_key = (*it);
+               std::string val_str;
+               int value;
+
+               script += "(" + attr_key + " ";
+               if (data.get(NULL, attr_key.c_str(), &val_str)) {       // string type data
+                       script += "\"" + val_str + "\"";
+               } else if (data.get(NULL, attr_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& cond_tmpl, ctx::json& data)
+{
+       std::string script = "(modify-instance [" + instance_name + "] ";
+       ctx::json attrs;
+       std::list<std::string> attr_keys;
+
+       cond_tmpl.get(NULL, "attributes", &attrs);
+       attrs.get_keys(&attr_keys);
+
+       // attributes
+       for (std::list<std::string>::iterator it = attr_keys.begin(); it != attr_keys.end(); ++it) {
+               std::string attr_key = (*it);
+               std::string val_str;
+               int value;
+
+               script += "(" + attr_key + " ";
+               if (data.get(NULL, attr_key.c_str(), &val_str)) {       // string type data
+                       script += "\"" + val_str + "\"";
+               } else  if (data.get(NULL, attr_key.c_str(), &value)) { // integer type data
+                       script += int_to_string(value);
+               } else {
+                       script += "nil";
+               }
+               script += ")";
+       }
+       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..4b2d1ac
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015 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& tmpl);
+               std::string generate_defclass(ctx::json& tmpl);
+               std::string generate_makeinstance(ctx::json& tmpl);
+               std::string generate_undefrule(std::string rule_id);
+               std::string generate_defrule(std::string rule_id, ctx::json& event_tmpl, ctx::json& rule, ctx::json& inst_names);
+               std::string generate_fact(std::string item_name, ctx::json& event_tmpl, ctx::json& option, ctx::json& data);
+               std::string generate_modifyinstance(std::string instance_name, ctx::json& cond_tmpl, 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..33ce780
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2015 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"
+#include <context_mgr.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)
+       : trigger(tr)
+{
+       submit_trigger_item();
+}
+
+ctx::trigger_timer::~trigger_timer()
+{
+       clear();
+}
+
+void ctx::trigger_timer::submit_trigger_item()
+{
+       context_manager::register_trigger_item(TIMER_EVENT_SUBJECT, OPS_SUBSCRIBE,
+                       "{"
+                               "\"TimeOfDay\":{\"type\":\"integer\",\"min\":0,\"max\":1439},"
+                               "\"DayOfWeek\":{\"type\":\"string\",\"values\":[\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\",\"Weekday\",\"Weekend\"]}"
+                       "}",
+                       NULL);
+
+       context_manager::register_trigger_item(TIMER_CONDITION_SUBJECT, OPS_READ,
+                       "{"
+                               "\"TimeOfDay\":{\"type\":\"integer\",\"min\":0,\"max\":1439},"
+                               "\"DayOfWeek\":{\"type\":\"string\",\"values\":[\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\",\"Weekday\",\"Weekend\"]},"
+                               "\"DayOfMonth\":{\"type\":\"integer\",\"min\":1,\"max\":31}"
+                       "}",
+                       NULL);
+
+}
+
+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("Time: %02d:%02d, Day of Week: %#x", 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);
+}
+
+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..9f62927
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 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;
+               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);
+               ~trigger_timer();
+               static void submit_trigger_item();
+
+               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..4dbd268
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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 "time/alarm"
+#define TIMER_CONDITION_SUBJECT "time/now"
+
+#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..8a84151
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2015 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;
+
+       delete rule_mgr;
+       rule_mgr = 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)
+{
+       context_fact *fact = new(std::nothrow) context_fact(req_id, error, subject, option, data);
+       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());
+}
+
+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* client = request->get_client();
+       if (client == NULL) {
+               request->reply(ERR_OPERATION_FAILED);
+               return;
+       }
+
+       const char* app_id = request->get_app_id();
+
+       int error = rule_mgr->add_rule(client, app_id, request->get_description(), &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_client();
+       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_client();
+       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_client();
+       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_client();
+       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_client();
+       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..1d4ba2a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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);
+
+               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..f4898c0
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2015 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/usr/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 << ",";
+
+                       char* buf = sqlite3_mprintf("%Q", s.c_str());
+                       IF_FAIL_RETURN_TAG(buf, false, _E, "Memory allocation failed");
+                       valstream << buf << ",";
+                       sqlite3_free(buf);
+               } 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);
+
+       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) {
+               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..d3f7a93
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 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..f68a26d
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2015 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 <app_manager.h>
+
+#include <types_internal.h>
+#include <dbus_listener_iface.h>
+#include "server.h"
+#include "client_request.h"
+#include "access_control/peer_creds.h"
+#include "dbus_server_impl.h"
+
+static bool conn_acquired = false;
+static bool name_acquired = false;
+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);
+
+       std::string smack_label;
+       pid_t pid;
+
+       if (!ctx::peer_creds::get(cookie, smack_label, pid)) {
+               _E("Peer credential failed");
+               g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
+               return;
+       }
+
+       char *app_id = NULL;
+       app_manager_get_app_id(pid, &app_id);
+       _D("AppId: %s", app_id);
+
+       ctx::client_request *request = new(std::nothrow) ctx::client_request(req_type, smack_label.c_str(), req_id, subject, input, sender, app_id, invocation);
+       if (!request) {
+               _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;
+       }
+
+       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);
+       }
+
+       conn_acquired = true;
+       dbus_connection = conn;
+
+       _I("Dbus connection acquired");
+
+       if (name_acquired)
+               ctx::server::activate();
+}
+
+static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
+{
+       name_acquired = true;
+       _SI("Dbus name acquired: %s", name);
+
+       if (conn_acquired)
+               ctx::server::activate();
+}
+
+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");
+
+       g_dbus_connection_call(dbus_connection, dest, DBUS_PATH, DBUS_IFACE,
+                       METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, NULL);
+}
+
+static void handle_call_result(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+       _I("Call %u done", *static_cast<unsigned int*>(user_data));
+
+       GDBusConnection *conn = G_DBUS_CONNECTION(source);
+       GError *error = NULL;
+       g_dbus_connection_call_finish(conn, res, &error);
+       HANDLE_GERROR(error);
+}
+
+void ctx::dbus_server_impl::call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param)
+{
+       IF_FAIL_VOID_TAG(dest && obj && iface && method, _E, "Parameter null");
+
+       static unsigned int call_count = 0;
+       ++call_count;
+
+       _SI("Call %u: %s, %s, %s.%s", call_count, dest, obj, iface, method);
+
+       g_dbus_connection_call(dbus_connection, dest, obj, iface, method, param, NULL,
+                       G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, handle_call_result, &call_count);
+}
+
+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);
+}
+
+void ctx::dbus_server::call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param)
+{
+       _instance->call(dest, obj, iface, method, param);
+}
diff --git a/src/dbus_server_impl.h b/src/dbus_server_impl.h
new file mode 100644 (file)
index 0000000..b9f1f7a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 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);
+               void call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param);
+               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);
+               void call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param);
+       }
+}      /* 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..df24f52
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 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_client()
+{
+       return _client.c_str();
+}
+
+const char* ctx::request_info::get_subject()
+{
+       return _subject.c_str();
+}
+
+ctx::json& ctx::request_info::get_description()
+{
+       return _description;
+}
+
+const char* ctx::request_info::get_app_id()
+{
+       return NULL;
+}
diff --git a/src/request.h b/src/request.h
new file mode 100644 (file)
index 0000000..755a979
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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_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 _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..1544bca
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2015 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 <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 "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::initialize()
+{
+       _I("Init MainLoop");
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_init();
+       ecore_main_loop_glib_integrate();
+#else
+       mainloop = g_main_loop_new(NULL, FALSE);
+#endif
+
+       _I("Init Dbus Connection");
+       dbus_handle = new(std::nothrow) ctx::dbus_server_impl();
+       IF_FAIL_VOID_TAG(dbus_handle, _E, "Memory allocation failed");
+
+       dbus_server::set_instance(dbus_handle);
+       IF_FAIL_VOID_TAG(dbus_handle->init(), _E, "Initialization Failed");
+
+       // Start the main loop
+       _I(CYAN("Launching Context-Service"));
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_main_loop_begin();
+#else
+       g_main_loop_run(mainloop);
+#endif
+}
+
+void ctx::server::activate()
+{
+       IF_FAIL_VOID(!started);
+
+       bool result = false;
+
+       _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");
+
+       started = true;
+       _I(CYAN("Context-Service Launched"));
+       return;
+
+CATCH:
+       _E(RED("Launching Failed"));
+
+       // Stop the main loop
+#ifdef _USE_ECORE_MAIN_LOOP_
+       ecore_main_loop_quit();
+#else
+       g_main_loop_quit(mainloop);
+#endif
+}
+
+void ctx::server::release()
+{
+       _I(CYAN("Terminating Context-Service"));
+       _I("Release Context Trigger");
+       if (trigger)
+               trigger->release();
+
+       _I("Release Analyzer Manager");
+       if (context_mgr)
+               context_mgr->release();
+
+       _I("Release Dbus Connection");
+       if (dbus_handle)
+               dbus_handle->release();
+
+       _I("Close the Database");
+       if (database_mgr)
+               database_mgr->release();
+
+       _I("Release Timer Manager");
+       if (timer_mgr)
+               timer_mgr->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;
+}
+
+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);
+       sigaction(SIGABRT, &signal_action, NULL);
+
+#if !defined(GLIB_VERSION_2_36)
+       g_type_init();
+#endif
+
+       ctx::server::initialize();
+       ctx::server::release();
+
+       return EXIT_SUCCESS;
+}
diff --git a/src/server.h b/src/server.h
new file mode 100644 (file)
index 0000000..d2e989e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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 initialize();
+               void activate();
+               void release();
+               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..9e3cd51
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2015 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..2b15f2e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 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__ */