--- /dev/null
+Mu-Woong Lee <muwoong.lee@samsung.com>
+Jihoon Park <jhp27.park@samsung.com>
+Somin Kim <somin926.kim@samsung.com>
--- /dev/null
+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})
--- /dev/null
+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.
+
--- /dev/null
+<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>
--- /dev/null
+[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
--- /dev/null
+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
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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, ¶m);
+
+ 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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(¤t_time);
+ tzset();
+ localtime_r(¤t_time, ¤t_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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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__ */