From db1c5eaf01761cba1b556d907a3d0446b84beb64 Mon Sep 17 00:00:00 2001 From: Mu-Woong Lee Date: Thu, 30 Mar 2017 15:59:32 +0900 Subject: [PATCH] Integrate the legacy context-service into the agent service (1/2) This patch just copies the legacy code files from Tizen 3.0 context-service. Change-Id: Ie615be0baaf8997c895eb42a537722a1f28f6267 Signed-off-by: Mu-Woong Lee --- src/agent/CMakeLists.txt | 2 +- src/agent/legacy/ClientRequest.cpp | 116 +++ src/agent/legacy/ClientRequest.h | 47 ++ src/agent/legacy/ContextManager.cpp | 237 ++++++ src/agent/legacy/ContextManager.h | 63 ++ src/agent/legacy/DBusServer.cpp | 228 ++++++ src/agent/legacy/DBusServer.h | 64 ++ src/agent/legacy/ProviderHandler.cpp | 310 ++++++++ src/agent/legacy/ProviderHandler.h | 78 ++ src/agent/legacy/ProviderLoader.cpp | 120 +++ src/agent/legacy/ProviderLoader.h | 63 ++ src/agent/legacy/Request.cpp | 66 ++ src/agent/legacy/Request.h | 56 ++ src/agent/legacy/Server.cpp | 170 +++++ src/agent/legacy/Server.h | 35 + src/agent/legacy/access_control/PeerCreds.cpp | 77 ++ src/agent/legacy/access_control/PeerCreds.h | 43 ++ src/agent/legacy/access_control/Privilege.cpp | 86 +++ src/agent/legacy/access_control/Privilege.h | 32 + src/agent/legacy/policy/PolicyManager.cpp | 69 ++ src/agent/legacy/policy/PolicyManager.h | 43 ++ src/agent/legacy/policy/PolicyRequest.cpp | 53 ++ src/agent/legacy/policy/PolicyRequest.h | 39 + src/agent/legacy/trigger/ActionManager.cpp | 210 ++++++ src/agent/legacy/trigger/ActionManager.h | 35 + src/agent/legacy/trigger/ContextMonitor.cpp | 318 ++++++++ src/agent/legacy/trigger/ContextMonitor.h | 92 +++ src/agent/legacy/trigger/FactRequest.cpp | 68 ++ src/agent/legacy/trigger/FactRequest.h | 46 ++ src/agent/legacy/trigger/FactTypes.h | 26 + src/agent/legacy/trigger/IContextListener.h | 38 + src/agent/legacy/trigger/Rule.cpp | 209 ++++++ src/agent/legacy/trigger/Rule.h | 80 ++ src/agent/legacy/trigger/RuleEvaluator.cpp | 385 ++++++++++ src/agent/legacy/trigger/RuleEvaluator.h | 53 ++ src/agent/legacy/trigger/RuleManager.cpp | 697 ++++++++++++++++++ src/agent/legacy/trigger/RuleManager.h | 71 ++ src/agent/legacy/trigger/TemplateManager.cpp | 196 +++++ src/agent/legacy/trigger/TemplateManager.h | 62 ++ src/agent/legacy/trigger/Trigger.cpp | 279 +++++++ src/agent/legacy/trigger/Trigger.h | 59 ++ 41 files changed, 5020 insertions(+), 1 deletion(-) create mode 100644 src/agent/legacy/ClientRequest.cpp create mode 100644 src/agent/legacy/ClientRequest.h create mode 100644 src/agent/legacy/ContextManager.cpp create mode 100644 src/agent/legacy/ContextManager.h create mode 100644 src/agent/legacy/DBusServer.cpp create mode 100644 src/agent/legacy/DBusServer.h create mode 100644 src/agent/legacy/ProviderHandler.cpp create mode 100644 src/agent/legacy/ProviderHandler.h create mode 100644 src/agent/legacy/ProviderLoader.cpp create mode 100644 src/agent/legacy/ProviderLoader.h create mode 100644 src/agent/legacy/Request.cpp create mode 100644 src/agent/legacy/Request.h create mode 100644 src/agent/legacy/Server.cpp create mode 100644 src/agent/legacy/Server.h create mode 100644 src/agent/legacy/access_control/PeerCreds.cpp create mode 100644 src/agent/legacy/access_control/PeerCreds.h create mode 100644 src/agent/legacy/access_control/Privilege.cpp create mode 100644 src/agent/legacy/access_control/Privilege.h create mode 100644 src/agent/legacy/policy/PolicyManager.cpp create mode 100644 src/agent/legacy/policy/PolicyManager.h create mode 100644 src/agent/legacy/policy/PolicyRequest.cpp create mode 100644 src/agent/legacy/policy/PolicyRequest.h create mode 100644 src/agent/legacy/trigger/ActionManager.cpp create mode 100644 src/agent/legacy/trigger/ActionManager.h create mode 100644 src/agent/legacy/trigger/ContextMonitor.cpp create mode 100644 src/agent/legacy/trigger/ContextMonitor.h create mode 100644 src/agent/legacy/trigger/FactRequest.cpp create mode 100644 src/agent/legacy/trigger/FactRequest.h create mode 100644 src/agent/legacy/trigger/FactTypes.h create mode 100644 src/agent/legacy/trigger/IContextListener.h create mode 100644 src/agent/legacy/trigger/Rule.cpp create mode 100644 src/agent/legacy/trigger/Rule.h create mode 100644 src/agent/legacy/trigger/RuleEvaluator.cpp create mode 100644 src/agent/legacy/trigger/RuleEvaluator.h create mode 100644 src/agent/legacy/trigger/RuleManager.cpp create mode 100644 src/agent/legacy/trigger/RuleManager.h create mode 100644 src/agent/legacy/trigger/TemplateManager.cpp create mode 100644 src/agent/legacy/trigger/TemplateManager.h create mode 100644 src/agent/legacy/trigger/Trigger.cpp create mode 100644 src/agent/legacy/trigger/Trigger.h diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt index 114362f..a97f2b1 100644 --- a/src/agent/CMakeLists.txt +++ b/src/agent/CMakeLists.txt @@ -4,7 +4,7 @@ SET(target "contextd-agent") SET(DEPS glib-2.0 gio-2.0 dlog capi-base-common) -FILE(GLOB_RECURSE SRCS *.cpp) +FILE(GLOB SRCS *.cpp) MESSAGE("Sources: ${SRCS}") INCLUDE(FindPkgConfig) diff --git a/src/agent/legacy/ClientRequest.cpp b/src/agent/legacy/ClientRequest.cpp new file mode 100644 index 0000000..e625bf0 --- /dev/null +++ b/src/agent/legacy/ClientRequest.cpp @@ -0,0 +1,116 @@ +/* + * 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 +#include +#include +#include +#include +#include "DBusServer.h" +#include "access_control/PeerCreds.h" +#include "ClientRequest.h" + +ctx::ClientRequest::ClientRequest(int type, int reqId, const char *subj, const char *desc, + ctx::Credentials *creds, const char *sender, GDBusMethodInvocation *inv) : + RequestInfo(type, reqId, subj, desc), + __credentials(creds), + __dbusSender(sender), + __invocation(inv) +{ +} + +ctx::ClientRequest::~ClientRequest() +{ + if (__invocation) + g_dbus_method_invocation_return_value(__invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + + delete __credentials; +} + +const ctx::Credentials* ctx::ClientRequest::getCredentials() +{ + return __credentials; +} + +const char* ctx::ClientRequest::getPackageId() +{ + if (__credentials) + return __credentials->packageId; + + return NULL; +} + +const char* ctx::ClientRequest::getClient() +{ + if (__credentials) + return __credentials->client; + + return NULL; +} + +bool ctx::ClientRequest::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::ClientRequest::reply(int error, ctx::Json& requestResult) +{ + IF_FAIL_RETURN(__invocation, true); + IF_FAIL_RETURN(error != ERR_NONE || __type != REQ_READ_SYNC, true); + + std::string result = requestResult.str(); + IF_FAIL_RETURN(!result.empty(), false); + + _I("Reply %#x", error); + _SD("Result: %s", result.c_str()); + + g_dbus_method_invocation_return_value(__invocation, g_variant_new("(iss)", error, result.c_str(), EMPTY_JSON_OBJECT)); + __invocation = NULL; + + return true; +} + +bool ctx::ClientRequest::reply(int error, ctx::Json& requestResult, ctx::Json& dataRead) +{ + if (__invocation == NULL) { + return publish(error, dataRead); + } + + std::string result = requestResult.str(); + std::string data = dataRead.str(); + IF_FAIL_RETURN(!result.empty() && !data.empty(), false); + + _I("Reply %#x", error); + _SD("Result: %s", result.c_str()); + _SD("Data: %s", data.c_str()); + + g_dbus_method_invocation_return_value(__invocation, g_variant_new("(iss)", error, result.c_str(), data.c_str())); + __invocation = NULL; + + return true; +} + +bool ctx::ClientRequest::publish(int error, ctx::Json& data) +{ + DBusServer::publish(__dbusSender, __reqId, __subject, error, data.str()); + return true; +} diff --git a/src/agent/legacy/ClientRequest.h b/src/agent/legacy/ClientRequest.h new file mode 100644 index 0000000..69d18e1 --- /dev/null +++ b/src/agent/legacy/ClientRequest.h @@ -0,0 +1,47 @@ +/* + * 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 +#include "Request.h" + +namespace ctx { + + class ClientRequest : public RequestInfo { + public: + ClientRequest(int type, int reqId, const char *subj, const char *desc, + Credentials *creds, const char *sender, GDBusMethodInvocation *inv); + ~ClientRequest(); + + const Credentials* getCredentials(); + const char* getPackageId(); + const char* getClient(); + bool reply(int error); + bool reply(int error, ctx::Json &requestResult); + bool reply(int error, ctx::Json &requestResult, ctx::Json &dataRead); + bool publish(int error, ctx::Json &data); + + private: + Credentials *__credentials; + std::string __dbusSender; + GDBusMethodInvocation *__invocation; + }; + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_CLIENT_REQUEST_H_ */ diff --git a/src/agent/legacy/ContextManager.cpp b/src/agent/legacy/ContextManager.cpp new file mode 100644 index 0000000..7d07966 --- /dev/null +++ b/src/agent/legacy/ContextManager.cpp @@ -0,0 +1,237 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include + +#include "access_control/Privilege.h" +#include "trigger/TemplateManager.h" +#include "Server.h" +#include "Request.h" +#include "ProviderHandler.h" +#include "ProviderLoader.h" +#include "ContextManager.h" + +using namespace ctx::trigger; +using namespace ctx; + +ContextManager::ContextManager() +{ + ContextProvider::__setContextManager(this); + CustomRegister::__setCustomRegister(this); + ProviderLoader::init(); +} + +ContextManager::~ContextManager() +{ + release(); +} + +bool ContextManager::init() +{ + return true; +} + +void ContextManager::release() +{ + ProviderHandler::purge(); +} + +void ContextManager::assignRequest(RequestInfo* request) +{ + std::string subject = request->getSubject(); + if (subject.empty()) { + _W("Empty subject name"); + request->reply(ERR_NOT_SUPPORTED); + delete request; + return; + } + + ProviderHandler *handle = ProviderHandler::getInstance(subject, true); + if (!handle || !handle->isSupported()) { + request->reply(ERR_NOT_SUPPORTED); + delete request; + return; + } + + if (request->getType() != REQ_SUPPORT && !handle->isAllowed(request->getCredentials())) { + _W("Permission denied"); + request->reply(ERR_PERMISSION_DENIED); + delete request; + return; + } + + switch (request->getType()) { + case REQ_SUBSCRIBE: + handle->subscribe(request); + break; + case REQ_UNSUBSCRIBE: + handle->unsubscribe(request); + break; + case REQ_READ: + case REQ_READ_SYNC: + handle->read(request); + break; + case REQ_WRITE: + handle->write(request); + break; + case REQ_SUPPORT: + request->reply(ERR_NONE); + delete request; + break; + default: + _E("Invalid type of request"); + delete request; + } +} + +bool ContextManager::isSupported(const char *subject) +{ + ProviderHandler *handle = ProviderHandler::getInstance(subject, true); + + if (!handle) + return false; + + return handle->isSupported(); +} + +bool ContextManager::isAllowed(const Credentials *creds, const char *subject) +{ + IF_FAIL_RETURN(creds, true); /* In case internal requests */ + + ProviderHandler *handle = ProviderHandler::getInstance(subject, true); + + if (!handle) + return false; + + return handle->isAllowed(creds); +} + +void ContextManager::__publish(const char* subject, Json &option, int error, Json &dataUpdated) +{ + _I("Publishing '%s'", subject); + _J("Option", option); + + ProviderHandler *handle = ProviderHandler::getInstance(subject, false); + IF_FAIL_VOID_TAG(handle, _W, "No corresponding provider"); + + handle->publish(option, error, dataUpdated); +} + +void ContextManager::__replyToRead(const char* subject, Json &option, int error, Json &dataRead) +{ + _I("Sending data of '%s'", subject); + _J("Option", option); + _J("Data", dataRead); + + ProviderHandler *handle = ProviderHandler::getInstance(subject, false); + IF_FAIL_VOID_TAG(handle, _W, "No corresponding provider"); + + handle->replyToRead(option, error, dataRead); +} + +struct PublishedData { + int type; + ContextManager *mgr; + std::string subject; + int error; + Json option; + Json data; + PublishedData(int t, ContextManager *m, const char* s, Json& o, int e, Json& d) + : type(t), mgr(m), subject(s), error(e) + { + option = o.str(); + data = d.str(); + } +}; + +gboolean ContextManager::__threadSwitcher(gpointer data) +{ + PublishedData *tuple = static_cast(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->__replyToRead(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data); + break; + default: + _W("Invalid type"); + } + + delete tuple; + return FALSE; +} + +bool ContextManager::publish(const char* subject, Json& option, int error, Json& dataUpdated) +{ + IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter"); + + PublishedData *tuple = new(std::nothrow) PublishedData(REQ_SUBSCRIBE, this, subject, option, error, dataUpdated); + IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed"); + + g_idle_add(__threadSwitcher, tuple); + + return true; +} + +bool ContextManager::replyToRead(const char* subject, Json& option, int error, Json& dataRead) +{ + IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter"); + + PublishedData *tuple = new(std::nothrow) PublishedData(REQ_READ, this, subject, option, error, dataRead); + IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed"); + + g_idle_add(__threadSwitcher, tuple); + + return true; +} + +bool ContextManager::popTriggerTemplate(std::string &subject, int &operation, Json &attribute, Json &option) +{ + return ProviderLoader::popTriggerTemplate(subject, operation, attribute, option); +} + +/* Only for explicit request of custom provider */ +bool ContextManager::registerCustomProvider(const char* subject, int operation, ctx::Json &attribute, ctx::Json &option, const char* owner) +{ + IF_FAIL_RETURN_TAG(ProviderHandler::getInstance(subject, true), false, _E, "Register provider failed"); + + TemplateManager* tmplMgr = TemplateManager::getInstance(); + IF_FAIL_RETURN_TAG(tmplMgr, false, _E, "Memory allocation failed"); + tmplMgr->registerTemplate(subject, operation, attribute, option, owner); + + return true; +} + +bool ContextManager::unregisterCustomProvider(const char* subject) +{ + TemplateManager* tmplMgr = TemplateManager::getInstance(); + IF_FAIL_RETURN_TAG(tmplMgr, false, _E, "Memory allocation failed"); + tmplMgr->unregisterTemplate(subject); + + int error = ProviderHandler::unregisterCustomProvider(subject); + IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Unregister provider failed"); + + return true; +} diff --git a/src/agent/legacy/ContextManager.h b/src/agent/legacy/ContextManager.h new file mode 100644 index 0000000..9be24c1 --- /dev/null +++ b/src/agent/legacy/ContextManager.h @@ -0,0 +1,63 @@ +/* + * 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_MANAGER_H_ +#define _CONTEXT_CONTEXT_MANAGER_H_ + +#include +#include +#include + +namespace ctx { + + /* Forward declaration */ + class Credentials; + class RequestInfo; + + class ContextManager : public IContextManager, ICustomRegister { + public: + ~ContextManager(); + + bool init(); + void release(); + + void assignRequest(ctx::RequestInfo *request); + bool isSupported(const char *subject); + bool isAllowed(const Credentials *creds, const char *subject); + + /* From the interface class */ + bool publish(const char *subject, ctx::Json &option, int error, ctx::Json &dataUpdated); + bool replyToRead(const char *subject, ctx::Json &option, int error, ctx::Json &dataRead); + bool registerCustomProvider(const char* subject, int operation, ctx::Json &attribute, ctx::Json &option, const char* owner); + bool unregisterCustomProvider(const char* subject); + + bool popTriggerTemplate(std::string &subject, int &operation, Json &attribute, Json &option); + + private: + ContextManager(); + + static gboolean __threadSwitcher(gpointer data); + + void __publish(const char *subject, ctx::Json &option, int error, ctx::Json &dataUpdated); + void __replyToRead(const char *subject, ctx::Json &option, int error, ctx::Json &dataRead); + + friend class Server; + + }; /* class ContextManager */ + +} /* namespace ctx */ + +#endif /* _CONTEXT_CONTEXT_MANAGER_H_ */ diff --git a/src/agent/legacy/DBusServer.cpp b/src/agent/legacy/DBusServer.cpp new file mode 100644 index 0000000..ff43612 --- /dev/null +++ b/src/agent/legacy/DBusServer.cpp @@ -0,0 +1,228 @@ +/* + * 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 +#include + +#include +#include +#include "Server.h" +#include "ClientRequest.h" +#include "access_control/PeerCreds.h" +#include "DBusServer.h" + +using namespace ctx; + +static const gchar __introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +DBusServer *DBusServer::__theInstance = NULL; + +DBusServer::DBusServer() : + __owner(-1), + __connection(NULL), + __nodeInfo(NULL) +{ +} + +DBusServer::~DBusServer() +{ + __release(); +} + +void DBusServer::__processRequest(const char *sender, GVariant *param, GDBusMethodInvocation *invocation) +{ + gint reqType = 0; + const gchar *cookie = NULL; + gint reqId = 0; + const gchar *subject = NULL; + const gchar *input = NULL; + + g_variant_get(param, "(i&si&s&s)", &reqType, &cookie, &reqId, &subject, &input); + IF_FAIL_VOID_TAG(reqType > 0 && reqId > 0 && cookie && subject && input, _E, "Invalid request"); + + _I("[%d] ReqId: %d, Subject: %s", reqType, reqId, subject); + _SI("Input: %s", input); + + Credentials *creds = NULL; + + if (!peer_creds::get(__connection, sender, cookie, &creds)) { + _E("Peer credentialing failed"); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + return; + } + + ClientRequest *request = new(std::nothrow) ClientRequest(reqType, reqId, subject, input, creds, sender, 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)); + delete creds; + return; + } + + Server::sendRequest(request); +} + +void DBusServer::__reply(GDBusMethodInvocation *invocation, int error) +{ + g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", error)); +} + +void DBusServer::__onMethodCalled(GDBusConnection *conn, const gchar *sender, + const gchar *path, const gchar *iface, const gchar *name, + GVariant *param, GDBusMethodInvocation *invocation, gpointer userData) +{ + IF_FAIL_VOID_TAG(STR_EQ(path, DBUS_PATH), _W, "Invalid path: %s", path); + IF_FAIL_VOID_TAG(STR_EQ(iface, DBUS_IFACE), _W, "Invalid interface: %s", path); + + if (STR_EQ(name, METHOD_REQUEST)) { + __theInstance->__processRequest(sender, param, invocation); + } else { + __theInstance->__reply(invocation, ERR_NONE); + } +} + +void DBusServer::__onBusAcquired(GDBusConnection *conn, const gchar *name, gpointer userData) +{ + GDBusInterfaceVTable vtable; + vtable.method_call = __onMethodCalled; + vtable.get_property = NULL; + vtable.set_property = NULL; + + guint regId = g_dbus_connection_register_object(conn, DBUS_PATH, + __theInstance->__nodeInfo->interfaces[0], &vtable, NULL, NULL, NULL); + + if (regId <= 0) { + _E("Failed to acquire dbus"); + raise(SIGTERM); + } + + __theInstance->__connection = conn; + _I("Dbus connection acquired"); +} + +void DBusServer::__onNameAcquired(GDBusConnection *conn, const gchar *name, gpointer userData) +{ + _SI("Dbus name acquired: %s", name); + Server::activate(); +} + +void DBusServer::__onNameLost(GDBusConnection *conn, const gchar *name, gpointer userData) +{ + _E("Dbus name lost"); + raise(SIGTERM); +} + +void DBusServer::__onCallDone(GObject *source, GAsyncResult *res, gpointer userData) +{ + _I("Call %u done", *static_cast(userData)); + + GDBusConnection *conn = G_DBUS_CONNECTION(source); + GError *error = NULL; + g_dbus_connection_call_finish(conn, res, &error); + HANDLE_GERROR(error); +} + +bool DBusServer::__init() +{ + __nodeInfo = g_dbus_node_info_new_for_xml(__introspection_xml, NULL); + IF_FAIL_RETURN_TAG(__nodeInfo != NULL, false, _E, "Initialization failed"); + + __owner = g_bus_own_name(G_BUS_TYPE_SESSION, DBUS_DEST, G_BUS_NAME_OWNER_FLAGS_NONE, + __onBusAcquired, __onNameAcquired, __onNameLost, NULL, NULL); + + __theInstance = this; + return true; +} + +void DBusServer::__release() +{ + if (__connection) { + g_dbus_connection_flush_sync(__connection, NULL, NULL); + } + + if (__owner > 0) { + g_bus_unown_name(__owner); + __owner = 0; + } + + if (__connection) { + g_dbus_connection_close_sync(__connection, NULL, NULL); + g_object_unref(__connection); + __connection = NULL; + } + + if (__nodeInfo) { + g_dbus_node_info_unref(__nodeInfo); + __nodeInfo = NULL; + } +} + +void DBusServer::__publish(const char *dest, int reqId, const char *subject, int error, const char *data) +{ + _SI("Publish: %s, %d, %s, %#x, %s", dest, reqId, subject, error, data); + + GVariant *param = g_variant_new("(isis)", reqId, subject, error, data); + IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed"); + + g_dbus_connection_call(__connection, dest, DBUS_PATH, DBUS_IFACE, + METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, NULL); +} + +void DBusServer::__call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param) +{ + static unsigned int callCount = 0; + ++callCount; + + _SI("Call %u: %s, %s, %s.%s", callCount, dest, obj, iface, method); + + g_dbus_connection_call(__connection, dest, obj, iface, method, param, NULL, + G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, __onCallDone, &callCount); +} + +void DBusServer::publish(std::string dest, int reqId, std::string subject, int error, std::string data) +{ + IF_FAIL_VOID_TAG(__theInstance, _E, "Not initialized"); + __theInstance->__publish(dest.c_str(), reqId, subject.c_str(), error, data.c_str()); +} + +void DBusServer::call(std::string dest, std::string obj, std::string iface, std::string method, GVariant *param) +{ + IF_FAIL_VOID_TAG(__theInstance, _E, "Not initialized"); + __theInstance->__call(dest.c_str(), obj.c_str(), iface.c_str(), method.c_str(), param); +} diff --git a/src/agent/legacy/DBusServer.h b/src/agent/legacy/DBusServer.h new file mode 100644 index 0000000..c4b1c32 --- /dev/null +++ b/src/agent/legacy/DBusServer.h @@ -0,0 +1,64 @@ +/* + * 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_H_ +#define _CONTEXT_DBUS_SERVER_H_ + +#include +#include +#include + +namespace ctx { + + class DBusServer { + public: + ~DBusServer(); + + static void publish(std::string dest, int reqId, std::string subject, int error, std::string data); + static void call(std::string dest, std::string obj, std::string iface, std::string method, GVariant *param); + + private: + DBusServer(); + + static void __onMethodCalled(GDBusConnection *conn, const gchar *sender, + const gchar *path, const gchar *iface, const gchar *name, + GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data); + static void __onBusAcquired(GDBusConnection *conn, const gchar *name, gpointer userData); + static void __onNameAcquired(GDBusConnection *conn, const gchar *name, gpointer userData); + static void __onNameLost(GDBusConnection *conn, const gchar *name, gpointer userData); + static void __onCallDone(GObject *source, GAsyncResult *res, gpointer userData); + + bool __init(); + void __release(); + void __publish(const char *dest, int reqId, const char *subject, int error, const char *data); + void __call(const char *dest, const char *obj, const char *iface, const char *method, GVariant *param); + + void __processRequest(const char *sender, GVariant *param, GDBusMethodInvocation *invocation); + void __reply(GDBusMethodInvocation *invocation, int error); + + static DBusServer *__theInstance; + + guint __owner; + GDBusConnection *__connection; + GDBusNodeInfo *__nodeInfo; + + friend class Server; + + }; /* class ctx::DBusServer */ + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_DBUS_SERVER_H_ */ diff --git a/src/agent/legacy/ProviderHandler.cpp b/src/agent/legacy/ProviderHandler.cpp new file mode 100644 index 0000000..2ce7f69 --- /dev/null +++ b/src/agent/legacy/ProviderHandler.cpp @@ -0,0 +1,310 @@ +/* + * 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 +#include +#include +#include "access_control/Privilege.h" +#include "Request.h" +#include "ProviderHandler.h" + +#define DELETE_DELAY 20 + +using namespace ctx; + +std::map ProviderHandler::__instanceMap; + +ProviderHandler::ProviderHandler(const std::string &subject) : + __subject(subject), + __deleteScheduled(false) +{ + _D("Subject: %s", __subject.c_str()); +} + +ProviderHandler::~ProviderHandler() +{ + _D("Subject: %s", __subject.c_str()); + + for (RequestInfo*& info : __subscribeRequests) { + delete info; + } + __subscribeRequests.clear(); + + for (RequestInfo*& info : __readRequests) { + delete info; + } + __readRequests.clear(); + + delete __provider; +} + +/* TODO: Return proper error code */ +ProviderHandler* ProviderHandler::getInstance(std::string subject, bool force) +{ + InstanceMap::iterator it = __instanceMap.find(subject); + + if (it != __instanceMap.end()) + return it->second; + + if (!force) + return NULL; + + ProviderHandler *handle = new(std::nothrow) ProviderHandler(subject); + IF_FAIL_RETURN_TAG(handle, NULL, _E, "Memory allocation failed"); + + if (!handle->__loadProvider()) { + delete handle; + return NULL; + } + + __instanceMap[subject] = handle; + + return handle; +} + +void ProviderHandler::purge() +{ + for (InstanceMap::iterator it = __instanceMap.begin(); it != __instanceMap.end(); ++it) { + delete it->second; + } + + __instanceMap.clear(); +} + +int ProviderHandler::unregisterCustomProvider(std::string subject) +{ + InstanceMap::iterator it = __instanceMap.find(subject); + IF_FAIL_RETURN_TAG(it != __instanceMap.end(), ERR_NOT_SUPPORTED, _E, "'%s' not found", subject.c_str()); + + __instanceMap.erase(subject); + delete it->second; + + _D("'%s' unregistered", subject.c_str()); + return ERR_NONE; +} + +bool ProviderHandler::isSupported() +{ + /* If idle, self destruct */ + __scheduleToDelete(); + + return __provider->isSupported(); +} + +bool ProviderHandler::isAllowed(const Credentials *creds) +{ + /* If idle, self destruct */ + __scheduleToDelete(); + + IF_FAIL_RETURN(creds, true); /* In case of internal requests */ + + std::vector priv; + __provider->getPrivilege(priv); + + for (unsigned int i = 0; i < priv.size(); ++i) { + if (!privilege_manager::isAllowed(creds, priv[i])) + return false; + } + + return true; +} + +void ProviderHandler::subscribe(RequestInfo *request) +{ + _I(CYAN("'%s' subscribes '%s' (RID-%d)"), request->getClient(), __subject.c_str(), request->getId()); + + Json requestResult; + int error = __provider->subscribe(request->getDescription().str(), &requestResult); + + if (!request->reply(error, requestResult) || error != ERR_NONE) { + delete request; + /* If idle, self destruct */ + __scheduleToDelete(); + return; + } + + __subscribeRequests.push_back(request); +} + +void ProviderHandler::unsubscribe(RequestInfo *request) +{ + _I(CYAN("'%s' unsubscribes '%s' (RID-%d)"), request->getClient(), __subject.c_str(), request->getId()); + + /* Search the subscribe request to be removed */ + auto target = __findRequest(__subscribeRequests, request->getClient(), request->getId()); + if (target == __subscribeRequests.end()) { + _W("Unknown request"); + delete request; + return; + } + + /* Keep the pointer to the request found */ + RequestInfo *reqFound = *target; + + /* Remove the request from the list */ + __subscribeRequests.erase(target); + + /* Check if there exist the same requests */ + if (__findRequest(__subscribeRequests, reqFound->getDescription()) != __subscribeRequests.end()) { + /* Do not stop detecting the subject */ + _D("A same request from '%s' exists", reqFound->getClient()); + request->reply(ERR_NONE); + delete request; + delete reqFound; + return; + } + + /* Stop detecting the subject */ + int error = __provider->unsubscribe(reqFound->getDescription()); + request->reply(error); + delete request; + delete reqFound; + + /* If idle, self destruct */ + __scheduleToDelete(); +} + +void ProviderHandler::read(RequestInfo *request) +{ + _I(CYAN("'%s' reads '%s' (RID-%d)"), request->getClient(), __subject.c_str(), request->getId()); + + Json requestResult; + int error = __provider->read(request->getDescription().str(), &requestResult); + + if (!request->reply(error, requestResult) || error != ERR_NONE) { + delete request; + /* If idle, self destruct */ + __scheduleToDelete(); + return; + } + + __readRequests.push_back(request); +} + +void ProviderHandler::write(RequestInfo *request) +{ + _I(CYAN("'%s' writes '%s' (RID-%d)"), request->getClient(), __subject.c_str(), request->getId()); + + Json requestResult; + request->getDescription().set(NULL, KEY_CLIENT_PKG_ID, request->getPackageId()? request->getPackageId() : "SYSTEM"); + int error = __provider->write(request->getDescription(), &requestResult); + + request->reply(error, requestResult); + delete request; + + /* If idle, self destruct */ + __scheduleToDelete(); +} + +bool ProviderHandler::publish(Json &option, int error, Json &dataUpdated) +{ + auto end = __subscribeRequests.end(); + auto target = __findRequest(__subscribeRequests.begin(), end, option); + + while (target != end) { + if (!(*target)->publish(error, dataUpdated)) { + return false; + } + target = __findRequest(++target, end, option); + } + + return true; +} + +bool ProviderHandler::replyToRead(Json &option, int error, Json &dataRead) +{ + auto end = __readRequests.end(); + auto target = __findRequest(__readRequests.begin(), end, option); + auto prev = target; + + Json dummy; + + while (target != end) { + (*target)->reply(error, dummy, dataRead); + prev = target; + target = __findRequest(++target, end, option); + + delete *prev; + __readRequests.erase(prev); + } + + /* If idle, self destruct */ + __scheduleToDelete(); + + return true; +} + +bool ProviderHandler::__loadProvider() +{ + __provider = __loader.load(__subject.c_str()); + return (__provider != NULL); +} + +bool ProviderHandler::__idle() +{ + return __subscribeRequests.empty() && __readRequests.empty(); +} + +void ProviderHandler::__scheduleToDelete() +{ + if (__provider->unloadable() && !__deleteScheduled && __idle()) { + __deleteScheduled = true; + g_timeout_add_seconds(DELETE_DELAY, __deletor, this); + _D("Delete scheduled for '%s' (%#x)", __subject.c_str(), this); + } +} + +gboolean ProviderHandler::__deletor(gpointer data) +{ + ProviderHandler *handle = static_cast(data); + + if (handle->__idle()) { + __instanceMap.erase(handle->__subject); + delete handle; + return FALSE; + } + + handle->__deleteScheduled = false; + return FALSE; +} + +ProviderHandler::RequestList::iterator +ProviderHandler::__findRequest(RequestList &reqList, Json &option) +{ + return __findRequest(reqList.begin(), reqList.end(), option); +} + +ProviderHandler::RequestList::iterator +ProviderHandler::__findRequest(RequestList &reqList, std::string client, int reqId) +{ + for (auto it = reqList.begin(); it != reqList.end(); ++it) { + if (client == (*it)->getClient() && reqId == (*it)->getId()) { + return it; + } + } + return reqList.end(); +} + +ProviderHandler::RequestList::iterator +ProviderHandler::__findRequest(RequestList::iterator begin, RequestList::iterator end, Json &option) +{ + for (auto it = begin; it != end; ++it) { + if (option == (*it)->getDescription()) { + return it; + } + } + return end; +} diff --git a/src/agent/legacy/ProviderHandler.h b/src/agent/legacy/ProviderHandler.h new file mode 100644 index 0000000..58225f7 --- /dev/null +++ b/src/agent/legacy/ProviderHandler.h @@ -0,0 +1,78 @@ +/* + * 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_PROVIDER_HANDLER_H_ +#define _CONTEXT_PROVIDER_HANDLER_H_ + +#include +#include +#include +#include +#include "ProviderLoader.h" + +namespace ctx { + + class Credentials; + class RequestInfo; + + class ProviderHandler { + typedef std::list RequestList; + typedef std::map InstanceMap; + + public: + bool isSupported(); + bool isAllowed(const Credentials *creds); + + void subscribe(RequestInfo *request); + void unsubscribe(RequestInfo *request); + void read(RequestInfo *request); + void write(RequestInfo *request); + + bool publish(ctx::Json &option, int error, ctx::Json &dataUpdated); + bool replyToRead(ctx::Json &option, int error, ctx::Json &dataRead); + + static ProviderHandler* getInstance(std::string subject, bool force); + static void purge(); + static int unregisterCustomProvider(std::string subject); + + private: + std::string __subject; + ContextProvider *__provider; + RequestList __subscribeRequests; + RequestList __readRequests; + ProviderLoader __loader; + bool __deleteScheduled; + + static InstanceMap __instanceMap; + + ProviderHandler(const std::string &subject); + ~ProviderHandler(); + + bool __loadProvider(); + bool __idle(); + void __scheduleToDelete(); + + RequestList::iterator __findRequest(RequestList &reqList, Json &option); + RequestList::iterator __findRequest(RequestList &reqList, std::string client, int reqId); + RequestList::iterator __findRequest(RequestList::iterator begin, RequestList::iterator end, Json &option); + + static gboolean __deletor(gpointer data); + + }; /* class ProviderHandler */ + +} /* namespace ctx */ + +#endif /* _CONTEXT_PROVIDER_HANDLER_H_ */ diff --git a/src/agent/legacy/ProviderLoader.cpp b/src/agent/legacy/ProviderLoader.cpp new file mode 100644 index 0000000..8a7d6a6 --- /dev/null +++ b/src/agent/legacy/ProviderLoader.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 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 +#include +#include +#include +#include "ProviderLoader.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +using namespace ctx; + +typedef ContextProvider* (*create_t)(const char *subject); + +std::map ProviderLoader::__providerLibMap; + +ProviderLoader::ProviderLoader() : + __soHandle(NULL) +{ +} + +ProviderLoader::~ProviderLoader() +{ + __unload(); +} + +ContextProvider* ProviderLoader::load(const char *subject) +{ + ProviderLibMap::iterator it = __providerLibMap.find(subject); + if (it == __providerLibMap.end()) { + _W("No provider for '%s'", subject); + return NULL; + } + + std::string path(_LIBDIR_ LIB_PREFIX); + path = path + it->second + LIB_EXTENSION; + + return __load(path.c_str(), subject); +} + +ContextProvider* ProviderLoader::__load(const char *soPath, const char *subject) +{ + _SI("Load '%s' from '%s'", subject, soPath); + + __soHandle = g_module_open(soPath, G_MODULE_BIND_LAZY); + IF_FAIL_RETURN_TAG(__soHandle, NULL, _E, "%s", g_module_error()); + + gpointer symbol; + + if (!g_module_symbol(__soHandle, "CreateProvider", &symbol) || symbol == NULL) { + _W("%s", g_module_error()); + g_module_close(__soHandle); + __soHandle = NULL; + return NULL; + } + + create_t create = reinterpret_cast(symbol); + + ContextProvider *prvd = create(subject); + if (!prvd) { + _W("No provider for '%s'", subject); + g_module_close(__soHandle); + __soHandle = NULL; + return NULL; + } + + return prvd; +} + +void ProviderLoader::__unload() +{ + if (!__soHandle) + return; + + g_module_close(__soHandle); + __soHandle = NULL; +} + +bool ProviderLoader::init() +{ + int size = ARRAY_SIZE(subjectLibraryList); + + for (int i = 0; i < size; ++i) { + __providerLibMap[subjectLibraryList[i].subject] = subjectLibraryList[i].library; + _SD("'%s' -> '%s'", subjectLibraryList[i].subject, subjectLibraryList[i].library); + } + + return true; +} + +bool ProviderLoader::popTriggerTemplate(std::string &subject, int &operation, Json &attribute, Json &option) +{ + static int i = 0; + static int size = ARRAY_SIZE(triggerTemplateList); + + if (i == size) + return false; + + subject = triggerTemplateList[i].subject; + operation = triggerTemplateList[i].operation; + attribute = triggerTemplateList[i].attribute; + option = triggerTemplateList[i].option; + + ++i; + return true; +} diff --git a/src/agent/legacy/ProviderLoader.h b/src/agent/legacy/ProviderLoader.h new file mode 100644 index 0000000..ab5a427 --- /dev/null +++ b/src/agent/legacy/ProviderLoader.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 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_PROVIDER_LOADER_H_ +#define _CONTEXT_PROVIDER_LOADER_H_ + +#include +#include + +namespace ctx { + + class ContextProvider; + + struct CompareSubjectName { + bool operator()(const char *left, const char *right) const { + while (*left != '\0' && *right != '\0') { + if (*left < *right) + return true; + if (*left > *right) + return false; + ++left; + ++right; + } + return false; + } + }; + + class ProviderLoader { + typedef std::map ProviderLibMap; + + public: + ProviderLoader(); + ~ProviderLoader(); + + ContextProvider* load(const char *subject); + + static bool init(); + static bool popTriggerTemplate(std::string &subject, int &operation, Json &attribute, Json &option); + + private: + ContextProvider* __load(const char *soPath, const char *subject); + void __unload(); + + GModule *__soHandle; + static ProviderLibMap __providerLibMap; + }; + +} + +#endif /* _CONTEXT_PROVIDER_LOADER_H_ */ diff --git a/src/agent/legacy/Request.cpp b/src/agent/legacy/Request.cpp new file mode 100644 index 0000000..d900fcc --- /dev/null +++ b/src/agent/legacy/Request.cpp @@ -0,0 +1,66 @@ +/* + * 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 +#include +#include "Request.h" + +ctx::RequestInfo::RequestInfo(int type, int reqId, const char* subj, const char* desc) : + __type(type), + __reqId(reqId), + __subject(subj), + __description(desc) +{ +} + +ctx::RequestInfo::~RequestInfo() +{ +} + +int ctx::RequestInfo::getType() +{ + return __type; +} + +int ctx::RequestInfo::getId() +{ + return __reqId; +} + +const ctx::Credentials* ctx::RequestInfo::getCredentials() +{ + return NULL; +} + +const char* ctx::RequestInfo::getPackageId() +{ + return NULL; +} + +const char* ctx::RequestInfo::getClient() +{ + return NULL; +} + +const char* ctx::RequestInfo::getSubject() +{ + return __subject.c_str(); +} + +ctx::Json& ctx::RequestInfo::getDescription() +{ + return __description; +} diff --git a/src/agent/legacy/Request.h b/src/agent/legacy/Request.h new file mode 100644 index 0000000..9750b2f --- /dev/null +++ b/src/agent/legacy/Request.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_REQUEST_INFO_H_ +#define _CONTEXT_REQUEST_INFO_H_ + +#include +#include + +namespace ctx { + + /* Forward declaration */ + class Credentials; + + class RequestInfo { + public: + RequestInfo(int type, int reqId, const char *subj, const char *desc); + virtual ~RequestInfo(); + + int getType(); + int getId(); + const char* getSubject(); + ctx::Json& getDescription(); + + virtual const Credentials* getCredentials(); + virtual const char* getPackageId(); + /* TODO: remove this getClient() */ + virtual const char* getClient(); + virtual bool reply(int error) = 0; + virtual bool reply(int error, ctx::Json &requestResult) = 0; + virtual bool reply(int error, ctx::Json &requestResult, ctx::Json &dataRead) = 0; + virtual bool publish(int error, ctx::Json &data) = 0; + + protected: + int __type; + int __reqId; + std::string __subject; + ctx::Json __description; + }; + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_REQUEST_INFO_H_ */ diff --git a/src/agent/legacy/Server.cpp b/src/agent/legacy/Server.cpp new file mode 100644 index 0000000..197b0d6 --- /dev/null +++ b/src/agent/legacy/Server.cpp @@ -0,0 +1,170 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include "DBusServer.h" +#include "ContextManager.h" +#include "policy/PolicyManager.h" +#include "trigger/Trigger.h" +#include "Server.h" + +#define RUN(L) g_main_loop_run((L)) +#define QUIT(L) if (g_main_loop_is_running((L)) == TRUE) g_main_loop_quit((L)) + + +static GMainLoop *mainloop = NULL; +static bool started = false; + +static ctx::ContextManager *__contextMgr = NULL; +static ctx::DBusServer *__dbusHandle = NULL; +static ctx::PolicyManager *__policyMgr = NULL; +static ctx::trigger::Trigger *__contextTrigger = NULL; +static ctx::TimerManager __timerManager; + +/* TODO: re-organize activation & deactivation processes */ +void ctx::Server::initialize() +{ + _I("Init Dbus Connection"); + __dbusHandle = new(std::nothrow) ctx::DBusServer(); + IF_FAIL_VOID_TAG(__dbusHandle, _E, "Memory allocation failed"); + IF_FAIL_VOID_TAG(__dbusHandle->__init(), _E, "Initialization Failed"); + + // Start the main loop + _I(CYAN("Launching Context-Service")); + RUN(mainloop); +} + +void ctx::Server::activate() +{ + IF_FAIL_VOID(!started); + + bool result = false; + + _I("Init Database Manager"); + IF_FAIL_CATCH(DatabaseManager::__init()); + + _I("Init Context Manager"); + __contextMgr = new(std::nothrow) ctx::ContextManager(); + IF_FAIL_CATCH_TAG(__contextMgr, _E, "Memory allocation failed"); + result = __contextMgr->init(); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Context Trigger"); + __contextTrigger = new(std::nothrow) ctx::trigger::Trigger(); + IF_FAIL_CATCH_TAG(__contextTrigger, _E, "Memory allocation failed"); + result = __contextTrigger->init(__contextMgr); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Policy Manager"); + __policyMgr = new(std::nothrow) ctx::PolicyManager(__contextMgr); + IF_FAIL_CATCH_TAG(__policyMgr, _E, "Memory allocation failed"); + + started = true; + _I(CYAN("Context-Service Launched")); + return; + +CATCH: + _E(RED("Launching Failed")); + + // Stop the main loop + QUIT(mainloop); +} + +void ctx::Server::release() +{ + _I(CYAN("Terminating Context-Service")); + started = false; + + _I("Release Policy Manager"); + delete __policyMgr; + + _I("Release Context Trigger"); + if (__contextTrigger) + __contextTrigger->release(); + + _I("Release Context Manager"); + if (__contextMgr) + __contextMgr->release(); + + _I("Release Database Manager"); + DatabaseManager::__release(); + + delete __contextTrigger; + delete __contextMgr; + delete __dbusHandle; +} + +static gboolean __postponeRequestAssignment(gpointer data) +{ + ctx::Server::sendRequest(static_cast(data)); + return FALSE; +} + +void ctx::Server::sendRequest(ctx::RequestInfo* request) +{ + if (!started) { + _W("Service not ready..."); + g_idle_add(__postponeRequestAssignment, request); + return; + } + + if (__contextTrigger->assignRequest(request)) + return; + + __contextMgr->assignRequest(request); +} + +static void __signalHandler(int signo) +{ + _I("SIGNAL %d received", signo); + + // Stop the main loop + QUIT(mainloop); +} + +int main(int argc, char* argv[]) +{ + static struct sigaction signalAction; + signalAction.sa_handler = __signalHandler; + sigemptyset(&signalAction.sa_mask); + + sigaction(SIGINT, &signalAction, NULL); + sigaction(SIGHUP, &signalAction, NULL); + sigaction(SIGTERM, &signalAction, NULL); + sigaction(SIGQUIT, &signalAction, NULL); + sigaction(SIGABRT, &signalAction, NULL); + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif + + _I("Init MainLoop"); + mainloop = g_main_loop_new(NULL, FALSE); + + ctx::Server::initialize(); + ctx::Server::release(); + + g_main_loop_unref(mainloop); + + return EXIT_SUCCESS; +} diff --git a/src/agent/legacy/Server.h b/src/agent/legacy/Server.h new file mode 100644 index 0000000..cd93bbf --- /dev/null +++ b/src/agent/legacy/Server.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_SERVER_H_ +#define _CONTEXT_SERVER_H_ + +namespace ctx { + + class RequestInfo; + + class Server { + public: + static void initialize(); + static void activate(); + static void release(); + static void sendRequest(RequestInfo* request); + + }; + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_SERVER_H_ */ diff --git a/src/agent/legacy/access_control/PeerCreds.cpp b/src/agent/legacy/access_control/PeerCreds.cpp new file mode 100644 index 0000000..336da5a --- /dev/null +++ b/src/agent/legacy/access_control/PeerCreds.cpp @@ -0,0 +1,77 @@ +/* + * 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 +#include +#include +#include +#include +#include "PeerCreds.h" + +ctx::Credentials::Credentials(char *pkgId, char *cli, char *sess, char *usr) : + packageId(pkgId), + client(cli), + session(sess), + user(usr) +{ +} + +ctx::Credentials::~Credentials() +{ + g_free(packageId); + g_free(client); + g_free(session); + g_free(user); +} + +bool ctx::peer_creds::get(GDBusConnection *connection, const char *uniqueName, const char *cookie, ctx::Credentials **creds) +{ + pid_t pid = 0; + char *app_id = NULL; + char *packageId = NULL; + gchar *client = NULL; + char *session = NULL; + gchar *user = NULL; + + int err = cynara_creds_gdbus_get_pid(connection, uniqueName, &pid); + IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, false, _E, "Peer credentialing failed"); + + session = cynara_session_from_pid(pid); + IF_FAIL_CATCH_TAG(session, _E, "Peer credentialing failed"); + + err = cynara_creds_gdbus_get_client(connection, uniqueName, CLIENT_METHOD_DEFAULT, &client); + IF_FAIL_CATCH_TAG(err == CYNARA_API_SUCCESS, _E, "Peer credentialing failed"); + + err = cynara_creds_gdbus_get_user(connection, uniqueName, USER_METHOD_DEFAULT, &user); + IF_FAIL_CATCH_TAG(err == CYNARA_API_SUCCESS, _E, "Peer credentialing failed"); + + app_manager_get_app_id(pid, &app_id); + package_manager_get_package_id_by_app_id(app_id, &packageId); + _D("AppId: %s, PackageId: %s", app_id, packageId); + g_free(app_id); + + *creds = new(std::nothrow) Credentials(packageId, client, session, user); + IF_FAIL_CATCH_TAG(*creds, _E, "Memory allocation failed"); + + return true; + +CATCH: + g_free(packageId); + g_free(client); + g_free(session); + g_free(user); + return false; +} diff --git a/src/agent/legacy/access_control/PeerCreds.h b/src/agent/legacy/access_control/PeerCreds.h new file mode 100644 index 0000000..255244e --- /dev/null +++ b/src/agent/legacy/access_control/PeerCreds.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_PEER_CREDENTIALS_H_ +#define _CONTEXT_PEER_CREDENTIALS_H_ + +#include +#include +#include + +namespace ctx { + + class Credentials { + public: + char *packageId; + char *client; /* default: smack label */ + char *session; + char *user; /* default: UID */ + Credentials(char *pkgId, char *cli, char *sess, char *usr); + ~Credentials(); + }; + + namespace peer_creds { + + bool get(GDBusConnection *connection, const char *uniqueName, const char *cookie, Credentials **creds); + + } /* namespace peer_creds */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_PEER_CREDENTIALS_H_ */ diff --git a/src/agent/legacy/access_control/Privilege.cpp b/src/agent/legacy/access_control/Privilege.cpp new file mode 100644 index 0000000..da2bdae --- /dev/null +++ b/src/agent/legacy/access_control/Privilege.cpp @@ -0,0 +1,86 @@ +/* + * 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 +#include +#include +#include "PeerCreds.h" +#include "Privilege.h" + +#define PRIV_PREFIX "http://tizen.org/privilege/" +#define CACHE_SIZE 100 + +class PermissionChecker { +private: + cynara *__cynara; + + PermissionChecker() + { + cynara_configuration *conf; + + int err = cynara_configuration_create(&conf); + IF_FAIL_VOID_TAG(err == CYNARA_API_SUCCESS, _E, "Cynara configuration creation failed"); + + err = cynara_configuration_set_cache_size(conf, CACHE_SIZE); + if (err != CYNARA_API_SUCCESS) { + _E("Cynara cache size set failed"); + cynara_configuration_destroy(conf); + return; + } + + err = cynara_initialize(&__cynara, conf); + cynara_configuration_destroy(conf); + if (err != CYNARA_API_SUCCESS) { + _E("Cynara initialization failed"); + __cynara = NULL; + return; + } + + _I("Cynara initialized"); + } + + ~PermissionChecker() + { + if (__cynara) + cynara_finish(__cynara); + + _I("Cynara deinitialized"); + } + +public: + static PermissionChecker& getInstance() + { + static PermissionChecker instance; + return instance; + } + + bool hasPermission(const ctx::Credentials *creds, const char *privilege) + { + IF_FAIL_RETURN_TAG(__cynara, false, _E, "Cynara not initialized"); + int ret = cynara_check(__cynara, creds->client, creds->session, creds->user, privilege); + return (ret == CYNARA_API_ACCESS_ALLOWED); + } +}; + +bool ctx::privilege_manager::isAllowed(const ctx::Credentials *creds, const char *privilege) +{ + IF_FAIL_RETURN(creds && privilege, true); + + std::string priv = PRIV_PREFIX; + priv += privilege; + + return PermissionChecker::getInstance().hasPermission(creds, priv.c_str()); +} diff --git a/src/agent/legacy/access_control/Privilege.h b/src/agent/legacy/access_control/Privilege.h new file mode 100644 index 0000000..6eb1198 --- /dev/null +++ b/src/agent/legacy/access_control/Privilege.h @@ -0,0 +1,32 @@ +/* + * 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_ + +namespace ctx { + + /* Forward declaration */ + class Credentials; + + namespace privilege_manager { + + bool isAllowed(const Credentials *creds, const char *privilege); + + } /* namespace privilege_manager */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_PRIVILEGE_MANAGER_H_ */ diff --git a/src/agent/legacy/policy/PolicyManager.cpp b/src/agent/legacy/policy/PolicyManager.cpp new file mode 100644 index 0000000..3f97344 --- /dev/null +++ b/src/agent/legacy/policy/PolicyManager.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 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 +#include +#include +#include +#include "../ContextManager.h" +#include "PolicyRequest.h" +#include "PolicyManager.h" + +using namespace ctx; + +PolicyManager::PolicyManager(ContextManager *contextMgr) : + __contextMgr(contextMgr) +{ + __subscribe(SUBJ_CUSTOM); + + __subscribe(SUBJ_APP_LOGGER); + __subscribe(SUBJ_BATTERY_LOGGER); + __subscribe(SUBJ_MEDIA_LOGGER); + __subscribe(SUBJ_PLACE_DETECTION); + __subscribe(SUBJ_STATE_WIFI); + + __subscribe(SUBJ_SENSOR_PEDOMETER); + __subscribe(SUBJ_SENSOR_PRESSURE); + __subscribe(SUBJ_SENSOR_SLEEP_MONITOR); + __subscribe(SUBJ_SENSOR_HEART_RATE); +} + +PolicyManager::~PolicyManager() +{ + for (auto &it : __subscriptionMap) { + PolicyRequest *req = new(std::nothrow) PolicyRequest(REQ_UNSUBSCRIBE, it.first, it.second, NULL); + if (!req) { + _E("Memory allocation failed"); + continue; + } + __contextMgr->assignRequest(req); + } + + __subscriptionMap.clear(); +} + +void PolicyManager::__subscribe(const char *subject) +{ + static int rid = 0; + ++rid; + + PolicyRequest *req = new(std::nothrow) PolicyRequest(REQ_SUBSCRIBE, rid, subject, NULL); + IF_FAIL_VOID_TAG(req, _E, "Memory allocation failed"); + + __contextMgr->assignRequest(req); + + __subscriptionMap[rid] = subject; +} diff --git a/src/agent/legacy/policy/PolicyManager.h b/src/agent/legacy/policy/PolicyManager.h new file mode 100644 index 0000000..ce9b315 --- /dev/null +++ b/src/agent/legacy/policy/PolicyManager.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 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_POLICY_MANAGER_H_ +#define _CONTEXT_POLICY_MANAGER_H_ + +#include + +namespace ctx { + + class ContextManger; + + class PolicyManager { + public: + ~PolicyManager(); + + private: + PolicyManager(ContextManager *contextMgr); + + void __subscribe(const char *subject); + + ContextManager *__contextMgr; + std::map __subscriptionMap; + + friend class Server; + }; + +} /* namespace ctx */ + +#endif /* _CONTEXT_POLICY_MANAGER_H_ */ diff --git a/src/agent/legacy/policy/PolicyRequest.cpp b/src/agent/legacy/policy/PolicyRequest.cpp new file mode 100644 index 0000000..63cf8ae --- /dev/null +++ b/src/agent/legacy/policy/PolicyRequest.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 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 "PolicyRequest.h" + +using namespace ctx; + +PolicyRequest::PolicyRequest(int type, int reqId, const char *subj, const char *desc) : + RequestInfo(type, reqId, subj, desc) +{ +} + +PolicyRequest::~PolicyRequest() +{ +} + +const char* PolicyRequest::getClient() +{ + return "POLICY"; +} + +bool PolicyRequest::reply(int error) +{ + return true; +} + +bool PolicyRequest::reply(int error, ctx::Json &requestResult) +{ + return true; +} + +bool PolicyRequest::reply(int error, ctx::Json &requestResult, ctx::Json &dataRead) +{ + return true; +} + +bool PolicyRequest::publish(int error, ctx::Json &data) +{ + return true; +} diff --git a/src/agent/legacy/policy/PolicyRequest.h b/src/agent/legacy/policy/PolicyRequest.h new file mode 100644 index 0000000..2ed28c7 --- /dev/null +++ b/src/agent/legacy/policy/PolicyRequest.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 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_POLICY_REQUEST_H_ +#define _CONTEXT_POLICY_REQUEST_H_ + +#include "../Request.h" + +namespace ctx { + + class PolicyRequest : public RequestInfo { + public: + PolicyRequest(int type, int reqId, const char *subj, const char *desc); + ~PolicyRequest(); + + const char* getClient(); + + bool reply(int error); + bool reply(int error, ctx::Json &requestResult); + bool reply(int error, ctx::Json &requestResult, ctx::Json &dataRead); + bool publish(int error, ctx::Json &data); + }; + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_POLICY_REQUEST_H_ */ diff --git a/src/agent/legacy/trigger/ActionManager.cpp b/src/agent/legacy/trigger/ActionManager.cpp new file mode 100644 index 0000000..b45671f --- /dev/null +++ b/src/agent/legacy/trigger/ActionManager.cpp @@ -0,0 +1,210 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../DBusServer.h" +#include "ActionManager.h" + +using namespace ctx; + +static void __triggerActionAppControl(Json& action); +static void __triggerActionNotification(Json& action, std::string pkgId); +static void __triggerActionDbusCall(Json& action); + +void trigger::action_manager::triggerAction(Json& action, std::string pkgId) +{ + std::list types; + action.getKeys(&types); + IF_FAIL_VOID_TAG(types.size() == 1, _E, "Invalid action"); + + std::string& type = *(types.begin()); + + if (type.compare(TRIG_RULE_KEY_APP_LAUNCH) == 0) { + __triggerActionAppControl(action); + } else if (type.compare(TRIG_RULE_KEY_NOTIFICATION) == 0) { + __triggerActionNotification(action, pkgId); + } else if (type.compare(TRIG_RULE_KEY_DBUS_CALL) == 0) { + __triggerActionDbusCall(action); + } +} + +void __triggerActionAppControl(Json& action) +{ + int error; + std::string appctlStr; + action.get(TRIG_RULE_KEY_APP_LAUNCH, TRIG_RULE_KEY_APP_LAUNCH_APP_CONTROL, &appctlStr); + + char* str = static_cast(malloc(appctlStr.length())); + if (str == NULL) { + _E("Memory allocation failed"); + return; + } + appctlStr.copy(str, appctlStr.length(), 0); + bundle_raw* encoded = reinterpret_cast(str); + bundle* appctlBundle = bundle_decode(encoded, appctlStr.length()); + + app_control_h app = NULL; + app_control_create(&app); + app_control_import_from_bundle(app, appctlBundle); + + 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(appctlBundle); + 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); + } +} + +void __triggerActionNotification(Json& action, std::string pkgId) +{ + int error; + notification_h notification = notification_create(NOTIFICATION_TYPE_NOTI); + std::string title; + if (action.get(TRIG_RULE_KEY_NOTIFICATION, TRIG_RULE_KEY_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(TRIG_RULE_KEY_NOTIFICATION, TRIG_RULE_KEY_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 imagePath; + if (action.get(TRIG_RULE_KEY_NOTIFICATION, TRIG_RULE_KEY_NOTI_ICON_PATH, &imagePath)) { + error = notification_set_image(notification, NOTIFICATION_IMAGE_TYPE_ICON, imagePath.c_str()); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set notification icon image failed(%d)", error); + } + } + + std::string appctlStr; + char* str = NULL; + bundle_raw* encoded = NULL; + bundle* appctlBundle = NULL; + app_control_h app = NULL; + if (action.get(TRIG_RULE_KEY_NOTIFICATION, TRIG_RULE_KEY_NOTI_APP_CONTROL, &appctlStr)) { + str = static_cast(malloc(appctlStr.length())); + if (str == NULL) { + _E("Memory allocation failed"); + notification_free(notification); + return; + } + appctlStr.copy(str, appctlStr.length(), 0); + encoded = reinterpret_cast(str); + appctlBundle = bundle_decode(encoded, appctlStr.length()); + + app_control_create(&app); + app_control_import_from_bundle(app, appctlBundle); + + 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 (!pkgId.empty()) { + error = notification_set_pkgname(notification, pkgId.c_str()); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set package id(%s) failed(%#x)", pkgId.c_str(), error); + } + } + + int soundOn = 0; + error = vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &soundOn); + if (error < 0) { + _E("vconf error (%d)", error); + } else if (soundOn) { + error = notification_set_sound(notification, NOTIFICATION_SOUND_TYPE_DEFAULT, NULL); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set notification sound failed(%d)", error); + } + } + + int vibrationOn = 0; + error = vconf_get_bool(VCONFKEY_SETAPPL_VIBRATION_STATUS_BOOL, &vibrationOn); + if (error < 0) { + _E("vconf error (%d)", error); + } else if (vibrationOn) { + 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(appctlBundle); + 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); + } +} + +void __triggerActionDbusCall(Json& action) +{ + std::string busName, object, iface, method; + GVariant *param = NULL; + + action.get(TRIG_RULE_KEY_DBUS_CALL, TRIG_RULE_KEY_DBUS_NAME, &busName); + IF_FAIL_VOID_TAG(!busName.empty(), _E, "No target bus name"); + + action.get(TRIG_RULE_KEY_DBUS_CALL, TRIG_RULE_KEY_DBUS_OBJECT, &object); + IF_FAIL_VOID_TAG(!object.empty(), _E, "No object path"); + + action.get(TRIG_RULE_KEY_DBUS_CALL, TRIG_RULE_KEY_DBUS_INTERFACE, &iface); + IF_FAIL_VOID_TAG(!iface.empty(), _E, "No interface name"); + + action.get(TRIG_RULE_KEY_DBUS_CALL, TRIG_RULE_KEY_DBUS_METHOD, &method); + IF_FAIL_VOID_TAG(!method.empty(), _E, "No method name"); + + action.get(TRIG_RULE_KEY_DBUS_CALL, TRIG_RULE_KEY_DBUS_PARAMETER, ¶m); + + DBusServer::call(busName, object, iface, method, param); +} diff --git a/src/agent/legacy/trigger/ActionManager.h b/src/agent/legacy/trigger/ActionManager.h new file mode 100644 index 0000000..273f450 --- /dev/null +++ b/src/agent/legacy/trigger/ActionManager.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_TRIGGER_ACTION_MANAGER_H_ +#define _CONTEXT_TRIGGER_ACTION_MANAGER_H_ + +namespace ctx { + /* Forward Declaration */ + class Json; + +namespace trigger { + + namespace action_manager { + + void triggerAction(Json& action, std::string pkgId); + + } /* namespace action_manager */ + +} /* namespace trigger*/ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_ACTION_MANAGER_H_ */ diff --git a/src/agent/legacy/trigger/ContextMonitor.cpp b/src/agent/legacy/trigger/ContextMonitor.cpp new file mode 100644 index 0000000..5dab7f5 --- /dev/null +++ b/src/agent/legacy/trigger/ContextMonitor.cpp @@ -0,0 +1,318 @@ +/* + * 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 +#include "../access_control/Privilege.h" +#include "../ContextManager.h" +#include "ContextMonitor.h" +#include "IContextListener.h" +#include "FactRequest.h" + +using namespace ctx; +using namespace trigger; + +static int __lastRid; +static int __lastErr; + +ContextMonitor *ContextMonitor::__instance = NULL; +ContextManager *ContextMonitor::__contextMgr = NULL; + +static int __generateReqId() +{ + static int reqId = 0; + + if (++reqId < 0) { + // Overflow handling + reqId = 1; + } + + return reqId; +} + +ContextMonitor::ContextMonitor() +{ +} + +ContextMonitor::~ContextMonitor() +{ +} + +void ContextMonitor::setContextManager(ContextManager* ctxMgr) +{ + __contextMgr = ctxMgr; +} + +ContextMonitor* ContextMonitor::getInstance() +{ + IF_FAIL_RETURN_TAG(__contextMgr, NULL, _E, "Context manager is needed"); + + IF_FAIL_RETURN(!__instance, __instance); + + __instance = new(std::nothrow) ContextMonitor(); + IF_FAIL_RETURN_TAG(__instance, NULL, _E, "Memory alllocation failed"); + + return __instance; +} + +void ContextMonitor::destroy() +{ + if (__instance) { + delete __instance; + __instance = NULL; + } +} + +int ContextMonitor::subscribe(int ruleId, std::string subject, Json option, IContextListener* listener) +{ + int reqId = __subscribe(subject.c_str(), &option, listener); + IF_FAIL_RETURN_TAG(reqId > 0, reqId, _E, "Subscribe event failed"); + _D(YELLOW("Subscribe event(rule%d). req%d"), ruleId, reqId); + + return ERR_NONE; +} + +int ContextMonitor::__subscribe(const char* subject, Json* option, IContextListener* listener) +{ + IF_FAIL_RETURN(subject, ERR_INVALID_PARAMETER); + + int rid = __findSub(REQ_SUBSCRIBE, subject, option); + if (rid > 0) { + __addListener(REQ_SUBSCRIBE, rid, listener); + _D("Duplicated request for %s", subject); + return rid; + } + + rid = __generateReqId(); + + FactRequest *req = new(std::nothrow) FactRequest(REQ_SUBSCRIBE, + rid, subject, option ? option->str().c_str() : NULL, this); + IF_FAIL_RETURN_TAG(req, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed"); + + __contextMgr->assignRequest(req); + __addSub(REQ_SUBSCRIBE, rid, subject, option, listener); + + if (__lastErr != ERR_NONE) { + __removeSub(REQ_SUBSCRIBE, rid); + _E("Subscription request failed: %#x", __lastErr); + return __lastErr; + } + + return rid; +} + +int ContextMonitor::unsubscribe(int ruleId, std::string subject, Json option, IContextListener* listener) +{ + int rid = __findSub(REQ_SUBSCRIBE, subject.c_str(), &option); + if (rid < 0) { + _D("Invalid unsubscribe request"); + return ERR_INVALID_PARAMETER; + } + + if (__removeListener(REQ_SUBSCRIBE, rid, listener) <= 0) { + __unsubscribe(subject.c_str(), rid); + } + _D(YELLOW("Unsubscribe event(rule%d). req%d"), ruleId, rid); + + return ERR_NONE; +} + +void ContextMonitor::__unsubscribe(const char *subject, int subscriptionId) +{ + FactRequest *req = new(std::nothrow) FactRequest(REQ_UNSUBSCRIBE, subscriptionId, subject, NULL, NULL); + IF_FAIL_VOID_TAG(req, _E, "Memory allocation failed"); + + __contextMgr->assignRequest(req); + __removeSub(REQ_SUBSCRIBE, subscriptionId); +} + +int ContextMonitor::read(std::string subject, Json option, IContextListener* listener) +{ + int reqId = __read(subject.c_str(), &option, listener); + IF_FAIL_RETURN_TAG(reqId > 0, ERR_OPERATION_FAILED, _E, "Read condition failed"); + _D(YELLOW("Read condition(%s). req%d"), subject.c_str(), reqId); + + return ERR_NONE; +} + +int ContextMonitor::__read(const char* subject, Json* option, IContextListener* listener) +{ + IF_FAIL_RETURN(subject, ERR_INVALID_PARAMETER); + + int rid = __findSub(REQ_READ, subject, option); + if (rid > 0) { + __addListener(REQ_READ, rid, listener); + _D("Duplicated request for %s", subject); + return rid; + } + + rid = __generateReqId(); + + FactRequest *req = new(std::nothrow) FactRequest(REQ_READ, + rid, subject, option ? option->str().c_str() : NULL, this); + IF_FAIL_RETURN_TAG(req, -1, _E, "Memory allocation failed"); + + __contextMgr->assignRequest(req); + __addSub(REQ_READ, rid, subject, option, listener); + + if (__lastErr != ERR_NONE) { + _E("Read request failed: %#x", __lastErr); + return -1; + } + + return rid; +} + +bool ContextMonitor::isSupported(std::string subject) +{ + return __contextMgr->isSupported(subject.c_str()); +} + +bool ContextMonitor::isAllowed(const char *client, const char *subject) +{ + //TODO: re-implement this in the proper 3.0 style + //return __contextMgr->isAllowed(client, subject); + return true; +} + +int ContextMonitor::__findSub(RequestType type, const char* subject, Json* option) +{ + // @return request id + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + Json jOpt; + if (option) { + jOpt = *option; + } + + for (auto it = map->begin(); it != map->end(); ++it) { + if ((*(it->second)).subject == subject && (*(it->second)).option == jOpt) { + return it->first; + } + } + + return -1; +} + +bool ContextMonitor::__addSub(RequestType type, int sid, const char* subject, Json* option, IContextListener* listener) +{ + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + SubscrInfo *info = new(std::nothrow) SubscrInfo(sid, subject, option); + IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed"); + info->listenerList.push_back(listener); + + map->insert(std::pair(sid, info)); + return true; +} + +void ContextMonitor::__removeSub(RequestType type, const char* subject, Json* option) +{ + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + Json jOpt; + if (option) { + jOpt = *option; + } + + for (auto it = map->begin(); it != map->end(); ++it) { + if ((*(it->second)).subject == subject && (*(it->second)).option == jOpt) { + delete it->second; + map->erase(it); + return; + } + } +} + +void ContextMonitor::__removeSub(RequestType type, int sid) +{ + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + SubscrInfo* info = map->at(sid); + info->listenerList.clear(); + + delete info; + map->erase(sid); + + return; +} + +int ContextMonitor::__addListener(RequestType type, int sid, IContextListener* listener) +{ + // @return number of listeners for the corresponding sid + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + auto it = map->find(sid); + + SubscrInfo* info = it->second; + info->listenerList.push_back(listener); + + return info->listenerList.size(); +} + +int ContextMonitor::__removeListener(RequestType type, int sid, IContextListener* listener) +{ + // @return number of listeners for the corresponding sid + std::map* map = (type == REQ_SUBSCRIBE)? &__subscrMap : &__readMap; + + auto it = map->find(sid); + + SubscrInfo* info = it->second; + + for (auto it2 = info->listenerList.begin(); it2 != info->listenerList.end(); ++it2) { + if (*it2 == listener) { + info->listenerList.erase(it2); + break; + } + } + + return info->listenerList.size(); +} + +void ContextMonitor::replyResult(int reqId, int error, Json* requestResult) +{ + _D("Request result received: %d", reqId); + + __lastRid = reqId; + __lastErr = error; +} + +void ContextMonitor::replyResult(int reqId, int error, const char* subject, Json* option, Json* fact) +{ + _D(YELLOW("Condition received: subject(%s), option(%s), fact(%s)"), subject, option->str().c_str(), fact->str().c_str()); + + auto it = __readMap.find(reqId); + IF_FAIL_VOID_TAG(it != __readMap.end(), _E, "Request id not found"); + + SubscrInfo* info = it->second; + for (auto it2 = info->listenerList.begin(); it2 != info->listenerList.end(); ++it2) { + (*it2)->onConditionReceived(subject, *option, *fact); + } + + __removeSub(REQ_READ, reqId); +} + +void ContextMonitor::publishFact(int reqId, int error, const char* subject, Json* option, Json* fact) +{ + _D(YELLOW("Event received: subject(%s), option(%s), fact(%s)"), subject, option->str().c_str(), fact->str().c_str()); + + auto it = __subscrMap.find(reqId); + IF_FAIL_VOID_TAG(it != __subscrMap.end(), _E, "Request id not found"); + + SubscrInfo* info = it->second; + for (auto it2 = info->listenerList.begin(); it2 != info->listenerList.end(); ++it2) { + (*it2)->onEventReceived(subject, *option, *fact); + } +} diff --git a/src/agent/legacy/trigger/ContextMonitor.h b/src/agent/legacy/trigger/ContextMonitor.h new file mode 100644 index 0000000..473cac2 --- /dev/null +++ b/src/agent/legacy/trigger/ContextMonitor.h @@ -0,0 +1,92 @@ +/* + * 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_CONTEXT_MONITOR_H_ +#define _CONTEXT_TRIGGER_CONTEXT_MONITOR_H_ + +#include +#include +#include +#include + +namespace ctx { + + class ContextManager; + +namespace trigger { + + class IContextListener; + class FactRequest; + + class ContextMonitor { + public: + static ContextMonitor* getInstance(); + static void setContextManager(ContextManager* ctxMgr); + static void destroy(); + + int subscribe(int ruleId, std::string subject, Json option, IContextListener* listener); + int unsubscribe(int ruleId, std::string subject, Json option, IContextListener* listener); + int read(std::string subject, Json option, IContextListener* listener); + bool isSupported(std::string subject); + bool isAllowed(const char *client, const char *subject); + + void replyResult(int reqId, int error, Json *requestResult = NULL); + void replyResult(int reqId, int error, const char *subject, Json *option, Json *fact); + void publishFact(int reqId, int error, const char *subject, Json *option, Json *fact); + + private: + ContextMonitor(); + ContextMonitor(const ContextMonitor& other); + ~ContextMonitor(); + + static ContextMonitor *__instance; + static ContextManager *__contextMgr; + + int __subscribe(const char* subject, Json* option, IContextListener* listener); + void __unsubscribe(const char *subject, int subscriptionId); + int __read(const char *subject, Json *option, IContextListener* listener); + + struct SubscrInfo { + int sid; + std::string subject; + Json option; + std::list listenerList; + + SubscrInfo(int id, const char *subj, Json *opt) : + sid(id), + subject(subj) + { + if (opt) + option = *opt; + } + }; + + std::map __subscrMap; + std::map __readMap; + + int __findSub(RequestType type, const char *subject, Json *option); + bool __addSub(RequestType type, int sid, const char *subject, Json *option, IContextListener* listener); + void __removeSub(RequestType type, const char *subject, Json *option); + void __removeSub(RequestType type, int sid); + int __addListener(RequestType type, int sid, IContextListener* listener); + int __removeListener(RequestType type, int sid, IContextListener* listener); + + }; /* class ContextMonitor */ + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_CONTEXT_MONITOR_H_ */ diff --git a/src/agent/legacy/trigger/FactRequest.cpp b/src/agent/legacy/trigger/FactRequest.cpp new file mode 100644 index 0000000..fc09d9d --- /dev/null +++ b/src/agent/legacy/trigger/FactRequest.cpp @@ -0,0 +1,68 @@ +/* + * 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 +#include "FactRequest.h" + +using namespace ctx; +using namespace ctx::trigger; + +FactRequest::FactRequest(int type, int reqId, const char* subj, const char* desc, ContextMonitor* ctxMonitor) : + RequestInfo(type, reqId, subj, desc), + __ctxMonitor(ctxMonitor), + __replied(false) +{ +} + +FactRequest::~FactRequest() +{ + reply(ERR_OPERATION_FAILED); +} + +const char* FactRequest::getClient() +{ + return "TRIGGER"; +} + +bool FactRequest::reply(int error) +{ + IF_FAIL_RETURN(!__replied && __ctxMonitor, true); + __ctxMonitor->replyResult(__reqId, error); + __replied = (error != ERR_NONE); + return true; +} + +bool FactRequest::reply(int error, Json& requestResult) +{ + IF_FAIL_RETURN(!__replied && __ctxMonitor, true); + __ctxMonitor->replyResult(__reqId, error, &requestResult); + __replied = (error != ERR_NONE); + return true; +} + +bool FactRequest::reply(int error, Json& requestResult, Json& dataRead) +{ + IF_FAIL_RETURN(!__replied && __ctxMonitor, true); + __ctxMonitor->replyResult(__reqId, error, __subject.c_str(), &getDescription(), &dataRead); + return (__replied = true); +} + +bool FactRequest::publish(int error, Json& data) +{ + IF_FAIL_RETURN(__ctxMonitor, true); + __ctxMonitor->publishFact(__reqId, error, __subject.c_str(), &getDescription(), &data); + return true; +} diff --git a/src/agent/legacy/trigger/FactRequest.h b/src/agent/legacy/trigger/FactRequest.h new file mode 100644 index 0000000..8c54fe8 --- /dev/null +++ b/src/agent/legacy/trigger/FactRequest.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_TRIGGER_FACT_REQUEST_H_ +#define _CONTEXT_TRIGGER_FACT_REQUEST_H_ + +#include "ContextMonitor.h" +#include "../Request.h" + +namespace ctx { + +namespace trigger { + + class FactRequest : public RequestInfo { + public: + FactRequest(int type, int reqId, const char* subj, const char* desc, ContextMonitor* ctxMonitor); + ~FactRequest(); + + const char* getClient(); + bool reply(int error); + bool reply(int error, ctx::Json& requestResult); + bool reply(int error, ctx::Json& requestResult, ctx::Json& dataRead); + bool publish(int error, ctx::Json& data); + + private: + ContextMonitor *__ctxMonitor; + bool __replied; + }; + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_FACT_REQUEST_H_ */ diff --git a/src/agent/legacy/trigger/FactTypes.h b/src/agent/legacy/trigger/FactTypes.h new file mode 100644 index 0000000..fe50f11 --- /dev/null +++ b/src/agent/legacy/trigger/FactTypes.h @@ -0,0 +1,26 @@ +/* + * 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_TYPES_H_ +#define _CONTEXT_TRIGGER_FACT_TYPES_H_ + +#define FACT_KEY_EVENT "event" +#define FACT_KEY_CONDITION "condition" +#define FACT_KEY_NAME "name" +#define FACT_KEY_OPTION "option" +#define FACT_KEY_DATA "data" + +#endif /* End of _CONTEXT_TRIGGER_FACT_TYPES_H_ */ diff --git a/src/agent/legacy/trigger/IContextListener.h b/src/agent/legacy/trigger/IContextListener.h new file mode 100644 index 0000000..826ab41 --- /dev/null +++ b/src/agent/legacy/trigger/IContextListener.h @@ -0,0 +1,38 @@ +/* + * 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_I_CONTEXT_LISTENER_H_ +#define _CONTEXT_TRIGGER_I_CONTEXT_LISTENER_H_ + +namespace ctx { + /* Forward Declaration */ + class Json; + +namespace trigger { + + class IContextListener { + public: + virtual ~IContextListener() {} + + virtual void onEventReceived(std::string name, Json option, Json data) = 0; + + virtual void onConditionReceived(std::string name, Json option, Json data) = 0; + }; + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_I_CONTEXT_LISTENER_H_ */ diff --git a/src/agent/legacy/trigger/Rule.cpp b/src/agent/legacy/trigger/Rule.cpp new file mode 100644 index 0000000..33b2ef8 --- /dev/null +++ b/src/agent/legacy/trigger/Rule.cpp @@ -0,0 +1,209 @@ +/* + * 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 +#include "Rule.h" +#include "ActionManager.h" +#include "RuleEvaluator.h" +#include "ContextMonitor.h" +#include "FactTypes.h" +#include "RuleManager.h" + +using namespace ctx; +using namespace ctx::trigger; + +RuleManager *Rule::__ruleMgr = NULL; + +Rule::Rule(int i, Json& d, const char* p, RuleManager* rm) : + __result(EMPTY_JSON_OBJECT), + id(i), + pkgId(p) +{ + // Rule manager + if (!__ruleMgr) { + __ruleMgr = rm; + } + + // Statement + __statement = d.str(); + + // Event + Json e; + d.get(NULL, TRIG_RULE_KEY_EVENT, &e); + __event = new(std::nothrow) ContextItem(e); + + // Condition + int condNum = d.getSize(NULL, TRIG_RULE_KEY_CONDITION); + for (int j = 0; j < condNum; j++) { + Json c; + d.getAt(NULL, TRIG_RULE_KEY_CONDITION, j, &c); + __condition.push_back(new(std::nothrow) ContextItem(c)); + } + + // Extra + Json extra; + d.get(NULL, _TRIG_RULE_KEY_EXTRA, &extra); + __extra = extra.str(); + + // Action + Json a; + d.get(NULL, TRIG_RULE_KEY_ACTION, &a); + __action = a.str(); +} + +Rule::~Rule() +{ + // Release resources + delete __event; + for (auto it = __condition.begin(); it != __condition.end(); ++it) { + delete *it; + } +} + +int Rule::start(void) +{ + // Subscribe event + int error = ContextMonitor::getInstance()->subscribe(id, __event->name, __event->option, this); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to start rule%d", id); + + return error; +} + +int Rule::stop(void) +{ + // Unsubscribe event + int error = ContextMonitor::getInstance()->unsubscribe(id, __event->name, __event->option, this); + if (error == ERR_NOT_SUPPORTED) { + _E("Stop rule%d (event not supported)"); + return ERR_NONE; + } + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to stop rule%d", id); + + return error; +} + +bool Rule::__setConditionOptionBasedOnEvent(Json& option) +{ + // Set condition option if it references event data + std::list optionKeys; + option.getKeys(&optionKeys); + + for (auto it = optionKeys.begin(); it != optionKeys.end(); ++it) { + std::string optKey = (*it); + + std::string optVal; + if (option.get(NULL, optKey.c_str(), &optVal)) { + if (optVal.find("?") != 0) { + continue; + } + + std::string eventKey = optVal.substr(1, optVal.length() - 1); + + std::string newStr; + int new_val; + if (__result.get(FACT_KEY_EVENT "." FACT_KEY_DATA, eventKey.c_str(), &newStr)) { + option.set(NULL, optKey.c_str(), newStr); + } else if (__result.get(FACT_KEY_EVENT "." FACT_KEY_DATA, eventKey.c_str(), &new_val)) { + option.set(NULL, optKey.c_str(), new_val); + } else { + _W("Failed to find '%s' in event data", eventKey.c_str()); + return false; + } + } + } + + return true; +} + +void Rule::onEventReceived(std::string name, Json option, Json data) +{ + if (__result != EMPTY_JSON_OBJECT) { + __clearResult(); + } + + // Check if creator package is uninstalled + if (RuleManager::isUninstalledPackage(pkgId)) { + _D("Creator(%s) of rule%d is uninstalled.", pkgId.c_str(), id); + g_idle_add(__handleUninstalledRule, &pkgId); + return; + } + + _D("Rule%d received event data", id); + + // Set event data + __result.set(FACT_KEY_EVENT, FACT_KEY_NAME, name); + __result.set(FACT_KEY_EVENT, FACT_KEY_OPTION, option); + __result.set(FACT_KEY_EVENT, FACT_KEY_DATA, data); + + if (__condition.size() == 0) { + __onContextDataPrepared(); + return; + } + + IF_FAIL_VOID_TAG(RuleEvaluator::evaluateRule(__statement, __result), _E, "Event not matched"); + + // Request read conditions + for (auto it = __condition.begin(); it != __condition.end(); ++it) { + Json condOption = (*it)->option.str(); + if (!__setConditionOptionBasedOnEvent(condOption)) { // condOption should be copy of original option. + __clearResult(); + return; + } + + int error = ContextMonitor::getInstance()->read((*it)->name.c_str(), condOption, this); + IF_FAIL_VOID_TAG(error == ERR_NONE, _E, "Failed to read condition"); + } + + // TODO timer set +} + +void Rule::onConditionReceived(std::string name, Json option, Json data) +{ + _D("Rule%d received condition data", id); + + // Set condition data + Json item; + item.set(NULL, FACT_KEY_NAME, name); + item.set(NULL, FACT_KEY_OPTION, option); + item.set(NULL, FACT_KEY_DATA, data); + __result.append(NULL, FACT_KEY_CONDITION, item); + + if (__result.getSize(NULL, FACT_KEY_CONDITION) == (int) __condition.size()) { + __onContextDataPrepared(); + } +} + +void Rule::__clearResult() +{ + __result = EMPTY_JSON_OBJECT; + // TODO timer cancel +} + +void Rule::__onContextDataPrepared(void) +{ + if (RuleEvaluator::evaluateRule(__statement, __result)) { + action_manager::triggerAction(__action, pkgId); + } + __clearResult(); +} + +gboolean Rule::__handleUninstalledRule(gpointer data) +{ + std::string* pkgId = static_cast(data); + __ruleMgr->handleRuleOfUninstalledPackage(*pkgId); + + return FALSE; +} diff --git a/src/agent/legacy/trigger/Rule.h b/src/agent/legacy/trigger/Rule.h new file mode 100644 index 0000000..1192b6e --- /dev/null +++ b/src/agent/legacy/trigger/Rule.h @@ -0,0 +1,80 @@ +/* + * 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_RULE_H_ +#define _CONTEXT_TRIGGER_RULE_H_ + +#include +#include +#include +#include "IContextListener.h" + +namespace ctx { + +namespace trigger { + + class RuleManager; + + class Rule : public IContextListener { + private: + struct ContextItem { + std::string name; + ctx::Json option; + ContextItem(ctx::Json item) { + std::list keyList; + item.getKeys(&keyList); + name = *keyList.begin(); + + ctx::Json o; + if (item.get(name.c_str(), TRIG_RULE_KEY_OPTION, &o)) + option = o.str(); + } + }; + + ctx::Json __statement; + ContextItem* __event; + std::list __condition; + ctx::Json __extra; + ctx::Json __action; + ctx::Json __result; + + static RuleManager* __ruleMgr; + + void __clearResult(void); + bool __setConditionOptionBasedOnEvent(ctx::Json& option); + void __onContextDataPrepared(void); + + static gboolean __handleUninstalledRule(gpointer data); + + public: + int id; + std::string pkgId; + + Rule(int i, ctx::Json& d, const char* p, RuleManager* rm); + ~Rule(); + + int start(void); + int stop(void); + + void onEventReceived(std::string name, ctx::Json option, ctx::Json data); + void onConditionReceived(std::string name, ctx::Json option, ctx::Json data); + + }; + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_RULE_H_ */ diff --git a/src/agent/legacy/trigger/RuleEvaluator.cpp b/src/agent/legacy/trigger/RuleEvaluator.cpp new file mode 100644 index 0000000..b0e5833 --- /dev/null +++ b/src/agent/legacy/trigger/RuleEvaluator.cpp @@ -0,0 +1,385 @@ +/* + * 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 +#include +#include +#include "RuleEvaluator.h" +#include "FactTypes.h" + +#define OPERATOR_EQ "==" +#define OPERATOR_NEQ "!=" +#define OPERATOR_LEQ "<=" +#define OPERATOR_GEQ ">=" +#define OPERATOR_LT "<" +#define OPERATOR_GT ">" + +using namespace ctx; +using namespace ctx::trigger; + +RuleEvaluator::RuleEvaluator() +{ +} + +template +bool RuleEvaluator::__evaluateSingleData(T factVal, Json& comparison, std::string op) +{ + T ruleVal; + comparison.get(NULL, TRIG_RULE_KEY_VALUE, &ruleVal); + + if (op == TRIG_RULE_OP_EQUAL_TO) { + return (ruleVal == factVal); + } else if (op == TRIG_RULE_OP_NOT_EQUAL_TO) { + return (ruleVal != factVal); + } else if (op == TRIG_RULE_OP_GREATER_THAN) { + return (ruleVal > factVal); + } else if (op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO) { + return (ruleVal >= factVal); + } else if (op == TRIG_RULE_OP_LESS_THAN) { + return (ruleVal < factVal); + } else if (op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) { + return (ruleVal <= factVal); + } + + return false; +} + +template +bool RuleEvaluator::__evaluateDualData(T factVal, Json& comparison, std::string op) +{ + T ruleVal1, ruleVal2; + comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, 0, &ruleVal1); + comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, 1, &ruleVal2); + + if (op == TRIG_RULE_OP_IN) { + return (ruleVal1 <= factVal && factVal <= ruleVal2); + } else if (op == TRIG_RULE_OP_NOT_IN) { + return (factVal < ruleVal1 || ruleVal2 < factVal); + } + + return false; +} + +template +bool RuleEvaluator::__evaluateMultipleData(T factVal, Json& comparison, std::string op) +{ + T ruleVal; + for (int i = 0; comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, i, &ruleVal); i++) { + if (ruleVal == factVal) { + if (op == TRIG_RULE_OP_ONE_OF) { + return true; + } else if (op == TRIG_RULE_OP_NONE_OF) { + return false; + } + } + } + + if (op == TRIG_RULE_OP_NONE_OF) { + return true; + } + + return false; +} + +template +bool RuleEvaluator::__evaluateData(T factVal, Json& comparison) +{ + std::string op; + comparison.get(NULL, TRIG_RULE_KEY_OPERATOR, &op); + + if (op == TRIG_RULE_OP_EQUAL_TO || op == TRIG_RULE_OP_NOT_EQUAL_TO || + op == TRIG_RULE_OP_GREATER_THAN || op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO || + op == TRIG_RULE_OP_LESS_THAN || op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) { + return __evaluateSingleData(factVal, comparison, op); + } else if (op == TRIG_RULE_OP_IN || op == TRIG_RULE_OP_NOT_IN) { + return __evaluateDualData(factVal, comparison, op); + } else if (op == TRIG_RULE_OP_ONE_OF || op == TRIG_RULE_OP_NONE_OF) { + return __evaluateMultipleData(factVal, comparison, op); + } + + return false; +} + +void RuleEvaluator::__replaceSingleDataReferences(Json& eventFactData, Json& ruleComp, std::string& dataKey) +{ + std::string refVal; + std::string eventRefStr; + int eventRefInt; + + if (!ruleComp.get(dataKey.c_str(), TRIG_RULE_KEY_VALUE, &refVal)) { + return; + } + + if (refVal.substr(0, 1) != TRIG_RULE_REF_KEY_PREFIX) { + return; + } + + std::string eventKey = refVal.substr(1, refVal.length() - 1); + if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) { + ruleComp.set(dataKey.c_str(), TRIG_RULE_KEY_VALUE, eventRefStr); + } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) { + ruleComp.set(dataKey.c_str(), TRIG_RULE_KEY_VALUE, eventRefInt); + } else { + _W("Attribute %s not found in event_data", eventKey.c_str()); + } +} + +void RuleEvaluator::__replaceMultipleDataReferences(Json& eventFactData, Json& ruleComp, std::string& dataKey) +{ + std::string refVal; + std::string eventRefStr; + int eventRefInt; + + for (int i = 0; ruleComp.getAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, &refVal); i++) { + if (refVal.substr(0, 1) != TRIG_RULE_REF_KEY_PREFIX) { + continue; + } + + std::string eventKey = refVal.substr(1, refVal.length() - 1); + if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) { + ruleComp.setAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, eventRefStr); + } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) { + ruleComp.setAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, eventRefInt); + } else { + _W("Attribute %s not found in event_data", eventKey.c_str()); + } + } +} + +void RuleEvaluator::__replaceDataReferences(Json eventFactData, Json& ruleComp) +{ + // Replace referencing data to actual value + std::list compKeys; + ruleComp.getKeys(&compKeys); + + for (auto it = compKeys.begin(); it != compKeys.end(); ++it) { + std::string dataKey = *it; + + std::string op; + ruleComp.get(dataKey.c_str(), TRIG_RULE_KEY_OPERATOR, &op); + + std::string val; + if (op == TRIG_RULE_OP_EQUAL_TO || op == TRIG_RULE_OP_NOT_EQUAL_TO || + op == TRIG_RULE_OP_GREATER_THAN || op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO || + op == TRIG_RULE_OP_LESS_THAN || op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) { + + __replaceSingleDataReferences(eventFactData, ruleComp, dataKey); + } else { + __replaceMultipleDataReferences(eventFactData, ruleComp, dataKey); + } + } +} + +bool RuleEvaluator::__evaluateItem(Json& factItem, Json& ruleItem, std::string logicalOp) +{ + std::string name; + factItem.get(NULL, FACT_KEY_NAME, &name); + + Json comparison; + ruleItem.get(name.c_str(), TRIG_RULE_KEY_COMPARISON, &comparison); + + std::list compKeys; + comparison.getKeys(&compKeys); + if (compKeys.size() == 0) { + return true; + } + + bool isConjunction = (TRIG_RULE_LOGICAL_CONJUNCTION == logicalOp); + + for (auto it = compKeys.begin(); it != compKeys.end(); ++it) { + std::string dataKey = *it; + + Json comp; + comparison.get(NULL, dataKey.c_str(), &comp); + + std::string factValStr; + int factValInt; + + bool result; + if (factItem.get(FACT_KEY_DATA, dataKey.c_str(), &factValStr)) { + result = __evaluateData(factValStr, comp); + } else if (factItem.get(FACT_KEY_DATA, dataKey.c_str(), &factValInt)) { + result = __evaluateData(factValInt, comp); + } else { + _W("Could not get value corresponding to data key %s", dataKey.c_str()); + result = false; + } + + if (isConjunction && !result) { + return false; + } else if (!isConjunction && result) { + return true; + } + } + return isConjunction; +} + +bool RuleEvaluator::__evaluateRuleEvent(Json& fact, Json& rule) +{ + Json factItem; + Json ruleItem; + fact.get(NULL, FACT_KEY_EVENT, &factItem); + rule.get(NULL, TRIG_RULE_KEY_EVENT, &ruleItem); + + std::string eventOp; + rule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_EVENT_LOGICAL_OP, &eventOp); + + return __evaluateItem(factItem, ruleItem, eventOp); +} + +Json RuleEvaluator::__getConditionFact(Json& fact, Json& ruleCond) +{ + std::list condKey; + ruleCond.getKeys(&condKey); + std::string ruleCondName = *(condKey.begin()); + + Json factCond; + for (int i = 0; fact.getAt(NULL, FACT_KEY_CONDITION, i, &factCond); i++) { + // Check if fact item name is matched with condition + std::string factCondName; + factCond.get(NULL, FACT_KEY_NAME, &factCondName); + if (factCondName != ruleCondName) { + continue; + } + + // Check if fact item option is mathced with condition + Json ruleCondOption; + Json factCondOption; + ruleCond.get(ruleCondName.c_str(), TRIG_RULE_KEY_OPTION, &ruleCondOption); + factCond.get(NULL, FACT_KEY_OPTION, &factCondOption); + if (factCondOption == ruleCondOption) { + return factCond; + } + } + + _W(YELLOW("find condition failed for condition")); + return EMPTY_JSON_OBJECT; +} + +bool RuleEvaluator::__evaluateRuleCondition(Json& fact, Json& rule) +{ + std::string ruleOp; + rule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_RULE_LOGICAL_OP, &ruleOp); + bool isConjunction = (TRIG_RULE_LOGICAL_CONJUNCTION == ruleOp); + + Json ruleCond; + for (int i = 0; rule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &ruleCond); i++) { + Json factCond = __getConditionFact(fact, ruleCond); + + bool result; + if (factCond == EMPTY_JSON_OBJECT) { + result = false; + } else { + std::string condOp; + rule.getAt(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_CONDITION_LOGICAL_OP, i, &condOp); + result = __evaluateItem(factCond, ruleCond, condOp); + } + + if (isConjunction && !result) { + return false; + } else if (!isConjunction && result) { + return true; + } + } + + return isConjunction; +} + +bool RuleEvaluator::__replaceOptionReferences(Json eventFactData, Json& ruleOption) +{ + // Replace referencing option to actual value + std::string refVal; + std::string eventRefStr; + int eventRefInt; + + std::list keyList; + ruleOption.getKeys(&keyList); + + for (std::list::iterator it = keyList.begin(); it != keyList.end(); ++it) { + std::string optionKey = *it; + + if (!ruleOption.get(NULL, optionKey.c_str(), &refVal)) { + continue; + } + if (!(refVal.substr(0, 1) == TRIG_RULE_REF_KEY_PREFIX)) { + continue; + } + + std::string eventKey = refVal.substr(1, refVal.length() - 1); + if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) { + ruleOption.set(NULL, optionKey.c_str(), eventRefStr); + } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) { + ruleOption.set(NULL, optionKey.c_str(), eventRefInt); + } else { + _W("Option %s not found in event_data", eventKey.c_str()); + return false; + } + } + + return true; +} + +bool RuleEvaluator::__replaceEventReferences(Json& fact, Json& rule) +{ + // Replace referencing data/option to actual value + Json eventFactData; + if (!fact.get(FACT_KEY_EVENT, FACT_KEY_DATA, &eventFactData)) { + _E("No event data found, error"); + return false; + } + + Json ruleCond; + for (int i = 0; rule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &ruleCond); i++) { + std::list condKey; + ruleCond.getKeys(&condKey); + std::string ruleCondName = *(condKey.begin()); + + Json ruleComp; + for (int j = 0; ruleCond.getAt(ruleCondName.c_str(), TRIG_RULE_KEY_COMPARISON, j, &ruleComp); j++) { + __replaceDataReferences(eventFactData, ruleComp); + } + + Json ruleOption; + if (ruleCond.get(ruleCondName.c_str(), TRIG_RULE_KEY_OPTION, &ruleOption)) { + __replaceOptionReferences(eventFactData, ruleOption); + } + } + + return true; +} + +bool RuleEvaluator::evaluateRule(Json rule, Json fact) +{ + _D("Rule is %s ", rule.str().c_str()); + _D("fact is %s ", fact.str().c_str()); + + RuleEvaluator eval; + bool ret; + Json tempJson; + if (fact.get(NULL, FACT_KEY_CONDITION, &tempJson)) { + Json ruleCopy(rule.str()); + if (!eval.__replaceEventReferences(fact, ruleCopy)) { + _W("Replace failed"); + } + ret = eval.__evaluateRuleCondition(fact, ruleCopy); + _D("Checking condition %s", ret ? "true" : "false"); + } else { + ret = eval.__evaluateRuleEvent(fact, rule); + _D("Checking event %s", ret ? "true" : "false"); + } + + return ret; +} diff --git a/src/agent/legacy/trigger/RuleEvaluator.h b/src/agent/legacy/trigger/RuleEvaluator.h new file mode 100644 index 0000000..3e79682 --- /dev/null +++ b/src/agent/legacy/trigger/RuleEvaluator.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_TRIGGER_RULE_EVALUATOR_H_ +#define _CONTEXT_TRIGGER_RULE_EVALUATOR_H_ + +namespace ctx { + + class Json; + +namespace trigger { + + class RuleEvaluator { + private: + RuleEvaluator(); + + bool __evaluateRuleEvent(ctx::Json& fact, ctx::Json& rule); + bool __evaluateRuleCondition(ctx::Json& fact, ctx::Json& rule); + bool __evaluateItem(ctx::Json& factItem, ctx::Json& ruleItem, std::string logicalOp); + template bool __evaluateData(T factVal, Json& comparison); + template bool __evaluateSingleData(T factVal, Json& comparison, std::string op); + template bool __evaluateDualData(T factVal, Json& comparison, std::string op); + template bool __evaluateMultipleData(T factVal, Json& comparison, std::string op); + + ctx::Json __getConditionFact(ctx::Json& fact, ctx::Json& ruleCond); + + bool __replaceEventReferences(ctx::Json& fact, ctx::Json& rule); + void __replaceDataReferences(ctx::Json eventFactData, ctx::Json& ruleComp); + void __replaceSingleDataReferences(ctx::Json& eventFactData, ctx::Json& ruleComp, std::string& dataKey); + void __replaceMultipleDataReferences(ctx::Json& eventFactData, ctx::Json& ruleComp, std::string& dataKey); + bool __replaceOptionReferences(ctx::Json eventFactData, ctx::Json& ruleOption); + + public: + static bool evaluateRule(ctx::Json rule, ctx::Json data); + }; + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_RULE_EVALUATOR_H_ */ diff --git a/src/agent/legacy/trigger/RuleManager.cpp b/src/agent/legacy/trigger/RuleManager.cpp new file mode 100644 index 0000000..11a9561 --- /dev/null +++ b/src/agent/legacy/trigger/RuleManager.cpp @@ -0,0 +1,697 @@ +/* + * 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 +#include +#include +#include +#include +#include "RuleManager.h" +#include "ContextMonitor.h" +#include "Rule.h" + +#define RULE_TABLE "ContextTriggerRule" +#define RULE_IDS_JSON "{ \"" TRIG_KEY_ENABLED_IDS "\" : [ ] , \"" TRIG_KEY_DISABLED_IDS "\" : [ ] }"; + +using namespace ctx; +using namespace ctx::trigger; + +static int __stringToInt(std::string str) +{ + int i; + std::istringstream convert(str); + + if (!(convert >> i)) + i = 0; + + return i; +} + +static std::string __intToString(int i) +{ + std::ostringstream convert; + convert << i; + std::string str = convert.str(); + return str; +} + +RuleManager::RuleManager() +{ +} + +RuleManager::~RuleManager() +{ + // Release rule instances + _D("Release rule instances"); + for (auto it = __ruleMap.begin(); it != __ruleMap.end(); ++it) { + Rule* rule = static_cast(it->second); + + int error = rule->stop(); + if (error != ERR_NONE) { + _E("Failed to stop rule%d", it->first); + } + + delete rule; + } + __ruleMap.clear(); +} + +bool RuleManager::init() +{ + bool ret; + int error; + + // Create tables into db (rule, template) + std::string q1 = std::string("status INTEGER DEFAULT 0 NOT NULL, creator TEXT DEFAULT '' NOT NULL,") + + "packageId TEXT DEFAULT '' NOT NULL, description TEXT DEFAULT ''," + + "details TEXT DEFAULT '' NOT NULL"; + ret = __dbManager.createTableSync(RULE_TABLE, q1.c_str(), NULL); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create rule table failed"); + + // Before re-enable rules, handle uninstalled app's rules + if (__getUninstalledApp() > 0) { + error = __clearRuleOfUninstalledPackage(true); + IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Failed to remove uninstalled apps' rules while initialization"); + } + ret = __reenableRule(); + + return ret; +} + +void RuleManager::handleRuleOfUninstalledPackage(std::string pkgId) +{ + __uninstalledPackages.insert(pkgId); + __clearRuleOfUninstalledPackage(); +} + +int RuleManager::__getUninstalledApp(void) +{ + // Return number of uninstalled apps + std::string q1 = "SELECT DISTINCT packageId FROM ContextTriggerRule"; + + std::vector record; + bool ret = __dbManager.executeSync(q1.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, -1, _E, "Query package ids of registered rules failed"); + + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + std::string pkgId; + elem.get(NULL, "packageId", &pkgId); + + if (isUninstalledPackage(pkgId)) { + __uninstalledPackages.insert(pkgId); + } + } + + return __uninstalledPackages.size(); +} + +bool RuleManager::isUninstalledPackage(std::string pkgId) +{ + IF_FAIL_RETURN_TAG(!pkgId.empty(), false, _D, "Empty package id"); + + package_info_h pkgInfo; + int error = package_manager_get_package_info(pkgId.c_str(), &pkgInfo); + + if (error == PACKAGE_MANAGER_ERROR_NONE) { + package_info_destroy(pkgInfo); + } else if (error == PACKAGE_MANAGER_ERROR_NO_SUCH_PACKAGE) { + // Uninstalled package found + _D("Uninstalled package found: %s", pkgId.c_str()); + return true; + } else { + _E("Failed to get package info(%s): %d", pkgId.c_str(), error); + } + + return false; +} + +int RuleManager::__clearRuleOfUninstalledPackage(bool isInit) +{ + if (__uninstalledPackages.size() <= 0) { + return ERR_NONE; + } + + int error; + bool ret; + + _D("Clear uninstalled packages' rule started"); + // Package list + std::string pkgList = "("; + std::set::iterator it = __uninstalledPackages.begin(); + pkgList += "packageId = '" + *it + "'"; + it++; + for (; it != __uninstalledPackages.end(); ++it) { + pkgList += " OR packageId = '" + *it + "'"; + } + pkgList += ")"; + + // After event received, disable all the enabled rules of uninstalled apps + if (!isInit) { + std::string q1 = "SELECT rowId FROM ContextTriggerRule WHERE status = 2 and ("; + q1 += pkgList; + q1 += ")"; + + std::vector record; + ret = __dbManager.executeSync(q1.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Failed to query enabled rules of uninstalled packages"); + + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + int ruleId; + elem.get(NULL, "rowId", &ruleId); + error = disableRule(ruleId); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to disable rule"); + } + _D("Uninstalled packages' rules are disabled"); + } + + // Delete rules of uninstalled packages from DB + std::string q2 = "DELETE FROM ContextTriggerRule WHERE " + pkgList; + std::vector dummy; + ret = __dbManager.executeSync(q2.c_str(), &dummy); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Failed to remove rules from db"); + _D("Uninstalled packages' rules are deleted from db"); + + __uninstalledPackages.clear(); + + return ERR_NONE; +} + +int RuleManager::pauseRuleWithItem(std::string& subject) +{ + std::string q = "SELECT rowId FROM ContextTriggerRule WHERE (status=2) AND (details LIKE '%\"ITEM_NAME\":\"" + subject + "\"%');"; + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Failed to query rowIds to be paused"); + IF_FAIL_RETURN(record.size() > 0, ERR_NONE); + + _D("Pause rules related to %s", subject.c_str()); + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + int rowId; + elem.get(NULL, "rowId", &rowId); + + int error = pauseRule(rowId); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to disable rules using custom item"); + } + + return ERR_NONE; +} + +int RuleManager::resumeRuleWithItem(std::string& subject) +{ + std::string q = "SELECT rowId FROM ContextTriggerRule WHERE (status=1) AND (details LIKE '%\"ITEM_NAME\":\"" + subject + "\"%');"; + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query paused rule ids failed"); + IF_FAIL_RETURN(record.size() > 0, ERR_NONE); + + _D("Resume rules related to %s", subject.c_str()); + std::string qRowId; + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + int rowId; + elem.get(NULL, "rowId", &rowId); + + int error = enableRule(rowId); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to resume rule"); + } + + return ERR_NONE; +} + +bool RuleManager::__reenableRule(void) +{ + int error; + std::string q = "SELECT rowId FROM ContextTriggerRule WHERE status = 2"; + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query rowIds of enabled rules failed"); + IF_FAIL_RETURN_TAG(record.size() > 0, true, _D, "No rule to re-enable"); + + _D(YELLOW("Re-enable rule started")); + + std::string qRowId; + qRowId.clear(); + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + int rowId; + elem.get(NULL, "rowId", &rowId); + + error = enableRule(rowId); + if (error == ERR_NOT_SUPPORTED) { + qRowId += "(rowId = " + __intToString(rowId) + ") OR "; + } else if (error != ERR_NONE) { + _E("Re-enable rule%d failed(%d)", rowId, error); + } + } + IF_FAIL_RETURN(!qRowId.empty(), true); + qRowId = qRowId.substr(0, qRowId.length() - 4); + + // For rules which is failed to re-enable + std::string qUpdate = "UPDATE ContextTriggerRule SET status = 1 WHERE " + qRowId; + std::vector record2; + ret = __dbManager.executeSync(qUpdate.c_str(), &record2); + IF_FAIL_RETURN_TAG(ret, false, _E, "Failed to update rules as paused"); + + return true; +} + +bool RuleManager::__ruleEquals(Json& lRule, Json& rRule) +{ + // Compare event + Json lEvent, rEvent; + lRule.get(NULL, TRIG_RULE_KEY_EVENT, &lEvent); + rRule.get(NULL, TRIG_RULE_KEY_EVENT, &rEvent); + + std::string lEOp, rEOp; + lRule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_EVENT_LOGICAL_OP, &lEOp); + rRule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_EVENT_LOGICAL_OP, &rEOp); + + if (lEOp != rEOp || lEvent != rEvent) + return false; + + // Compare conditions + int lCondCnt, rCondCnt; + lCondCnt = lRule.getSize(NULL, TRIG_RULE_KEY_CONDITION); + rCondCnt = rRule.getSize(NULL, TRIG_RULE_KEY_CONDITION); + if (lCondCnt != rCondCnt) + return false; + + std::string lOp, rOp; + lRule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_RULE_LOGICAL_OP, &lOp); + rRule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_RULE_LOGICAL_OP, &rOp); + if (lOp != rOp) + return false; + + for (int i = 0; i < lCondCnt; i++) { + bool found = false; + Json lCond; + lRule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &lCond); + + std::string lCOp; + lRule.getAt(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_CONDITION_LOGICAL_OP, i, &lCOp); + + for (int j = 0; j < lCondCnt; j++) { + Json rCond; + rRule.getAt(NULL, TRIG_RULE_KEY_CONDITION, j, &rCond); + + std::string rCOp; + rRule.getAt(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_CONDITION_LOGICAL_OP, j, &rCOp); + + if (lCOp == rCOp && lCond == rCond) { + found = true; + break; + } + } + if (!found) + return false; + } + + // Compare action + Json lAction, rAction; + lRule.get(NULL, TRIG_RULE_KEY_ACTION, &lAction); + rRule.get(NULL, TRIG_RULE_KEY_ACTION, &rAction); + if (lAction != rAction) + return false; + + return true; +} + +int64_t RuleManager::__getDuplicatedRuleId(std::string pkgId, Json& rule) +{ + std::string q = "SELECT rowId, description, details FROM ContextTriggerRule WHERE packageId = '"; + q += pkgId; + q += "'"; + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query rowId, details by package id failed"); + + std::string rDesc; + rule.get(NULL, TRIG_RULE_KEY_DESCRIPTION, &rDesc); + Json rDetails = rule.str(); + rDetails.remove(NULL, TRIG_RULE_KEY_DESCRIPTION); + + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + std::string dStr; + Json details; + + elem.get(NULL, "details", &dStr); + details = dStr; + + if (__ruleEquals(rDetails, details)) { + int64_t rowId; + elem.get(NULL, "rowId", &rowId); + + // Description comparison + std::string desc; + elem.get(NULL, "description", &desc); + if (rDesc.compare(desc)) { + // Only description is changed + std::string qUpdate = "UPDATE ContextTriggerRule SET description='" + rDesc + "' WHERE rowId = " + __intToString(rowId); + + std::vector dummy; + ret = __dbManager.executeSync(qUpdate.c_str(), &dummy); + if (ret) { + _D("Rule%lld description is updated", rowId); + } else { + _W("Failed to update description of rule%lld", rowId); + } + } + + return rowId; + } + } + + return -1; +} + +int RuleManager::__verifyRule(Json& rule, const char* creator) +{ + ContextMonitor* ctxMonitor = ContextMonitor::getInstance(); + IF_FAIL_RETURN_TAG(ctxMonitor, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed"); + + // Event + Json event; + rule.get(NULL, TRIG_RULE_KEY_EVENT, &event); + + std::list eventKey; + event.getKeys(&eventKey); + IF_FAIL_RETURN_TAG(eventKey.size() == 1, ERR_INVALID_PARAMETER, _E, "Invalid event"); + + std::string eventName = *eventKey.begin(); + IF_FAIL_RETURN_TAG(ctxMonitor->isSupported(eventName), ERR_NOT_SUPPORTED, _I, "Event(%s) is not supported", eventName.c_str()); + + if (!ctxMonitor->isAllowed(creator, eventName.c_str())) { + _W("Permission denied for '%s'", eventName.c_str()); + return ERR_PERMISSION_DENIED; + } + + // Conditions + Json cond; + for (int i = 0; rule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &cond); i++) { + std::list condKey; + cond.getKeys(&condKey); + IF_FAIL_RETURN_TAG(condKey.size() == 1, ERR_INVALID_PARAMETER, _E, "Invalid condition"); + + std::string condName = *condKey.begin(); + IF_FAIL_RETURN_TAG(ctxMonitor->isSupported(condName), ERR_NOT_SUPPORTED, _I, "Condition(%s) is not supported", condName.c_str()); + + if (!ctxMonitor->isAllowed(creator, condName.c_str())) { + _W("Permission denied for '%s'", condName.c_str()); + return ERR_PERMISSION_DENIED; + } + } + + return ERR_NONE; +} + +int RuleManager::addRule(std::string creator, const char* pkgId, Json rule, Json* ruleId) +{ + bool ret; + int64_t rid; + + // Check if all items are supported && allowed to access + int err = __verifyRule(rule, creator.c_str()); + IF_FAIL_RETURN(err == ERR_NONE, err); + + // Check if duplicated rule exits + if ((rid = __getDuplicatedRuleId(pkgId, rule)) > 0) { + // Save rule id + ruleId->set(NULL, TRIG_KEY_RULE_ID, rid); + _D("Duplicated rule found"); + return ERR_NONE; + } + + // Insert rule to rule table, get rule id + Json record; + std::string description; + rule.get(NULL, TRIG_RULE_KEY_DESCRIPTION, &description); + + Json details = rule.str(); + details.remove(NULL, TRIG_RULE_KEY_DESCRIPTION); + record.set(NULL, "details", details.str()); + + record.set(NULL, "creator", creator); + if (pkgId) { + record.set(NULL, "packageId", pkgId); + } + record.set(NULL, "description", description); + + ret = __dbManager.insertSync(RULE_TABLE, record, &rid); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert rule to db failed"); + + // Save rule id + ruleId->set(NULL, TRIG_KEY_RULE_ID, rid); + + _D("Add rule%d succeeded", (int)rid); + return ERR_NONE; +} + + +int RuleManager::removeRule(int ruleId) +{ + bool ret; + + // Delete rule from DB + std::string query = "DELETE FROM 'ContextTriggerRule' where rowId = "; + query += __intToString(ruleId); + + std::vector record; + ret = __dbManager.executeSync(query.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed"); + + _D("Remove rule%d succeeded", ruleId); + + return ERR_NONE; +} + +int RuleManager::enableRule(int ruleId) +{ + int error; + std::string query; + std::vector record; + std::vector dummy; + std::string pkgId; + Json jRule; + std::string tmp; + std::string idStr = __intToString(ruleId); + + Rule* rule; + + // Get rule Json by rule id; + query = "SELECT details, packageId FROM ContextTriggerRule WHERE rowId = "; + query += idStr; + error = (__dbManager.executeSync(query.c_str(), &record))? ERR_NONE : ERR_OPERATION_FAILED; + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Query rule by rule id failed"); + + record[0].get(NULL, "details", &tmp); + jRule = tmp; + record[0].get(NULL, "packageId", &pkgId); + + // Create a rule instance + rule = new(std::nothrow) Rule(ruleId, jRule, pkgId.c_str(), this); + IF_FAIL_RETURN_TAG(rule, ERR_OUT_OF_MEMORY, _E, "Failed to create rule instance"); + + // Start the rule + error = rule->start(); + IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Failed to start rule%d", ruleId); + + // Update db to set 'enabled' + query = "UPDATE ContextTriggerRule SET status = 2 WHERE rowId = "; + query += idStr; + error = (__dbManager.executeSync(query.c_str(), &dummy))? ERR_NONE : ERR_OPERATION_FAILED; + IF_FAIL_CATCH_TAG(error == ERR_NONE, _E, "Update db failed"); + + // Add rule instance to __ruleMap + __ruleMap[ruleId] = rule; + + _D(YELLOW("Enable Rule%d succeeded"), ruleId); + return ERR_NONE; + +CATCH: + delete rule; + rule = NULL; + + return error; +} + +int RuleManager::disableRule(int ruleId) +{ + bool ret; + int error; + + auto it = __ruleMap.find(ruleId); + bool paused = (it == __ruleMap.end()); + + // For 'enabled' rule, not 'paused' + if (!paused) { + // Stop the rule + Rule* rule = static_cast(it->second); + error = rule->stop(); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to stop rule%d", ruleId); + + // Remove rule instance from __ruleMap + delete rule; + __ruleMap.erase(it); + } + + // Update db to set 'disabled' // TODO skip while clear uninstalled rule + std::string query = "UPDATE ContextTriggerRule SET status = 0 WHERE rowId = "; + query += __intToString(ruleId); + std::vector record; + ret = __dbManager.executeSync(query.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed"); + + _D(YELLOW("Disable Rule%d succeeded"), ruleId); + return ERR_NONE; +} + +int RuleManager::pauseRule(int ruleId) +{ + bool ret; + int error; + + auto it = __ruleMap.find(ruleId); + IF_FAIL_RETURN_TAG(it != __ruleMap.end(), ERR_OPERATION_FAILED, _E, "Rule instance not found"); + + // Stop the rule + Rule* rule = static_cast(it->second); + error = rule->stop(); + IF_FAIL_RETURN_TAG(error == ERR_NONE, error, _E, "Failed to stop rule%d", ruleId); + + // Update db to set 'paused' + std::string query = "UPDATE ContextTriggerRule SET status = 1 WHERE rowId = "; + + query += __intToString(ruleId); + std::vector record; + ret = __dbManager.executeSync(query.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed"); + + // Remove rule instance from __ruleMap + delete rule; + __ruleMap.erase(it); + + _D(YELLOW("Pause Rule%d"), ruleId); + return ERR_NONE; +} + +int RuleManager::checkRule(std::string pkgId, int ruleId) +{ + // Get package id + std::string q = "SELECT packageId FROM ContextTriggerRule WHERE rowId ="; + q += __intToString(ruleId); + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query package id by rule id failed"); + + if (record.size() == 0) { + return ERR_NO_DATA; + } + + std::string p; + record[0].get(NULL, "packageId", &p); + + if (p.compare(pkgId) == 0) { + return ERR_NONE; + } + + return ERR_NO_DATA; +} + +bool RuleManager::isRuleEnabled(int ruleId) +{ + std::string q = "SELECT status FROM ContextTriggerRule WHERE rowId ="; + q += __intToString(ruleId); + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query enabled by rule id failed"); + + int status; + record[0].get(NULL, "status", &status); + + return (status != 0); +} + +int RuleManager::getRuleById(std::string pkgId, int ruleId, Json* requestResult) +{ + std::string q = "SELECT description FROM ContextTriggerRule WHERE (packageId = '"; + q += pkgId; + q += "') and (rowId = "; + q += __intToString(ruleId); + q += ")"; + + std::vector record; + bool ret = __dbManager.executeSync(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); + + (*requestResult).set(NULL, TRIG_KEY_RULE_ID, ruleId); + (*requestResult).set(NULL, TRIG_RULE_KEY_DESCRIPTION, description); + + return ERR_NONE; +} + +int RuleManager::getRuleIds(std::string pkgId, Json* requestResult) +{ + (*requestResult) = RULE_IDS_JSON; + + std::string q = "SELECT rowId, status FROM ContextTriggerRule WHERE (packageId = '"; + q += pkgId; + q += "')"; + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rules failed"); + + std::vector::iterator vecEnd = record.end(); + for (std::vector::iterator vecPos = record.begin(); vecPos != vecEnd; ++vecPos) { + Json elem = *vecPos; + std::string id; + int status; + + elem.get(NULL, "rowId", &id); + elem.get(NULL, "status", &status); + + if (status >= 1) { + (*requestResult).append(NULL, TRIG_KEY_ENABLED_IDS, __stringToInt(id)); + } else if (status == 0) { + (*requestResult).append(NULL, TRIG_KEY_DISABLED_IDS, __stringToInt(id)); + } + } + + return ERR_NONE; +} diff --git a/src/agent/legacy/trigger/RuleManager.h b/src/agent/legacy/trigger/RuleManager.h new file mode 100644 index 0000000..cf47eee --- /dev/null +++ b/src/agent/legacy/trigger/RuleManager.h @@ -0,0 +1,71 @@ +/* + * 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_RULE_MANAGER_H_ +#define _CONTEXT_TRIGGER_RULE_MANAGER_H_ + +#include +#include +#include + +namespace ctx { + + class Json; + +namespace trigger { + + class Trigger; + class Rule; + + class RuleManager { + public: + RuleManager(); + ~RuleManager(); + + bool init(); + int addRule(std::string creator, const char* pkgId, Json rule, Json* ruleId); + int removeRule(int ruleId); + int enableRule(int ruleId); + int disableRule(int ruleId); + int getRuleById(std::string pkgId, int ruleId, Json* requestResult); + int getRuleIds(std::string pkgId, Json* requestResult); + int checkRule(std::string pkgId, int ruleId); + bool isRuleEnabled(int ruleId); + int pauseRuleWithItem(std::string& subject); + int pauseRule(int ruleId); + int resumeRuleWithItem(std::string& subject); + void handleRuleOfUninstalledPackage(std::string pkgId); + + static bool isUninstalledPackage(std::string pkgId); + + private: + bool __reenableRule(void); + int __verifyRule(Json& rule, const char* creator); + int64_t __getDuplicatedRuleId(std::string pkgId, Json& rule); + bool __ruleEquals(Json& lRule, Json& rRule); + int __getUninstalledApp(void); + int __clearRuleOfUninstalledPackage(bool isInit = false); + void __applyTemplates(void); + + std::set __uninstalledPackages; + std::map __ruleMap; + DatabaseManager __dbManager; + }; /* class RuleManager */ + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_RULE_MANAGER_H_ */ diff --git a/src/agent/legacy/trigger/TemplateManager.cpp b/src/agent/legacy/trigger/TemplateManager.cpp new file mode 100644 index 0000000..65ddc39 --- /dev/null +++ b/src/agent/legacy/trigger/TemplateManager.cpp @@ -0,0 +1,196 @@ +/* + * 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 +#include +#include +#include "../ContextManager.h" +#include "RuleManager.h" +#include "TemplateManager.h" + +using namespace ctx; +using namespace ctx::trigger; + +TemplateManager *TemplateManager::__instance = NULL; +ContextManager *TemplateManager::__contextMgr = NULL; +RuleManager *TemplateManager::__ruleMgr = NULL; + +static std::string __intToString(int i) +{ + std::ostringstream convert; + convert << i; + std::string str = convert.str(); + return str; +} + +TemplateManager::TemplateManager() +{ +} + +TemplateManager::~TemplateManager() +{ +} + +void TemplateManager::setManager(ContextManager* ctxMgr, RuleManager* ruleMgr) +{ + __contextMgr = ctxMgr; + __ruleMgr = ruleMgr; +} + +TemplateManager* TemplateManager::getInstance() +{ + IF_FAIL_RETURN_TAG(__contextMgr, NULL, _E, "Context manager is needed"); + IF_FAIL_RETURN_TAG(__ruleMgr, NULL, _E, "Rule manager is needed"); + + IF_FAIL_RETURN(!__instance, __instance); + + __instance = new(std::nothrow) TemplateManager(); + IF_FAIL_RETURN_TAG(__instance, NULL, _E, "Memory alllocation failed"); + + return __instance; +} + +void TemplateManager::destroy() +{ + IF_FAIL_VOID(__instance); + + __instance->applyTemplates(); + + delete __instance; + __instance = NULL; +} + +bool TemplateManager::init() +{ + std::string q = std::string("CREATE TABLE IF NOT EXISTS ContextTriggerTemplate ") + + "(name TEXT DEFAULT '' NOT NULL PRIMARY KEY, operation INTEGER DEFAULT 3 NOT NULL, " + + "attributes TEXT DEFAULT '' NOT NULL, options TEXT DEFAULT '' NOT NULL, owner TEXT DEFAULT '' NOT NULL)"; + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create template table failed"); + + // Apply templates + applyTemplates(); + + return true; +} + +void TemplateManager::applyTemplates() +{ + std::string subject; + int operation; + Json attributes; + Json options; + std::string owner; + std::string query; + query.clear(); + + while (__contextMgr->popTriggerTemplate(subject, operation, attributes, options)) { + registerTemplate(subject, operation, attributes, options, ""); + } +} + +void TemplateManager::registerTemplate(std::string subject, int operation, Json attributes, Json options, std::string owner) +{ + _D("[Add template] Subject: %s, Ops: %d, Owner: %s", subject.c_str(), operation, owner.c_str()); + _J("Attr", attributes); + _J("Opt", options); + + std::string query = "UPDATE ContextTriggerTemplate SET operation=" + __intToString(operation) + + ", attributes='" + attributes.str() + "', options='" + options.str() + "', owner='" + owner + + "' WHERE name='" + subject + "'; "; + + query += "INSERT OR IGNORE INTO ContextTriggerTemplate (name, operation, attributes, options, owner) VALUES ('" + + subject + "', " + __intToString(operation) + ", '" + attributes.str() + "', '" + options.str() + "', '" + + owner + "'); "; + + std::vector record; + bool ret = __dbManager.executeSync(query.c_str(), &record); + IF_FAIL_VOID_TAG(ret, _E, "Update template db failed"); + + if (!owner.empty()) { + __ruleMgr->resumeRuleWithItem(subject); + } +} + +void TemplateManager::unregisterTemplate(std::string subject) +{ + _D("[Remove template] Subject: %s", subject.c_str()); + std::string query = "DELETE FROM ContextTriggerTemplate WHERE name = '" + subject + "'; "; + + std::vector record; + bool ret = __dbManager.executeSync(query.c_str(), &record); + IF_FAIL_VOID_TAG(ret, _E, "Update template db failed"); + + __ruleMgr->pauseRuleWithItem(subject); +} + + +std::string TemplateManager::__addTemplate(std::string &subject, int &operation, Json &attributes, Json &options, std::string &owner) +{ + _D("[Add template] Subject: %s, Ops: %d, Owner: %s", subject.c_str(), operation, owner.c_str()); + _J("Attr", attributes); + _J("Opt", options); + + std::string query = "UPDATE ContextTriggerTemplate SET operation=" + __intToString(operation) + + ", attributes='" + attributes.str() + "', options='" + options.str() + "', owner='" + owner + + "' WHERE name='" + subject + "'; "; + + query += "INSERT OR IGNORE INTO ContextTriggerTemplate (name, operation, attributes, options, owner) VALUES ('" + + subject + "', " + __intToString(operation) + ", '" + attributes.str() + "', '" + options.str() + "', '" + + owner + "'); "; + + return query; +} + +std::string TemplateManager::__removeTemplate(std::string &subject) +{ + _D("[Remove template] Subject: %s", subject.c_str()); + std::string query = "DELETE FROM ContextTriggerTemplate WHERE name = '" + subject + "'; "; + + return query; +} + +int TemplateManager::getTemplate(std::string &subject, Json* tmpl) +{ + if (!__contextMgr->isSupported(subject.c_str())) + return ERR_NOT_SUPPORTED; + + // Update latest template information + std::string q = "SELECT * FROM ContextTriggerTemplate WHERE name = '" + subject + "'"; + + std::vector record; + bool ret = __dbManager.executeSync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query template failed"); + IF_FAIL_RETURN_TAG(record.size() > 0, ERR_NOT_SUPPORTED, _E, "Template(%s) not found", subject.c_str()); + IF_FAIL_RETURN_TAG(record.size() == 1, ERR_OPERATION_FAILED, _E, "Tepmlate duplicated"); + + (*tmpl) = *record.begin(); + + std::string optStr; + std::string attrStr; + tmpl->get(NULL, TRIG_TMPL_KEY_OPTION, &optStr); + tmpl->get(NULL, TRIG_TMPL_KEY_ATTRIBUTE, &attrStr); + + Json opt = optStr; + Json attr = attrStr; + + tmpl->set(NULL, TRIG_TMPL_KEY_OPTION, opt); + tmpl->set(NULL, TRIG_TMPL_KEY_ATTRIBUTE, attr); + + return ERR_NONE; +} diff --git a/src/agent/legacy/trigger/TemplateManager.h b/src/agent/legacy/trigger/TemplateManager.h new file mode 100644 index 0000000..c91ef15 --- /dev/null +++ b/src/agent/legacy/trigger/TemplateManager.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_TRIGGER_TEMPLATE_MANAGER_H_ +#define _CONTEXT_TRIGGER_TEMPLATE_MANAGER_H_ + +#include +#include + +namespace ctx { + + class ContextManager; + +namespace trigger { + + class RuleManager; + + class TemplateManager { + public: + static TemplateManager* getInstance(); + static void setManager(ContextManager* ctxMgr, RuleManager* ruleMgr); + static void destroy(); + + bool init(); + void applyTemplates(); + int getTemplate(std::string &subject, Json* tmpl); + void registerTemplate(std::string subject, int operation, Json attributes, Json options, std::string owner); + void unregisterTemplate(std::string subject); + + private: + TemplateManager(); + TemplateManager(const TemplateManager& other); + ~TemplateManager(); + + static TemplateManager *__instance; + static ContextManager *__contextMgr; + static RuleManager *__ruleMgr; + + DatabaseManager __dbManager; + + std::string __addTemplate(std::string &subject, int &operation, Json &attributes, Json &options, std::string &owner); + std::string __removeTemplate(std::string &subject); + + }; /* class TemplateManager */ + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_TEMPLATE_MANAGER_H_ */ diff --git a/src/agent/legacy/trigger/Trigger.cpp b/src/agent/legacy/trigger/Trigger.cpp new file mode 100644 index 0000000..bb3c7aa --- /dev/null +++ b/src/agent/legacy/trigger/Trigger.cpp @@ -0,0 +1,279 @@ +/* + * 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 +#include +#include "Trigger.h" +#include "ContextMonitor.h" +#include "TemplateManager.h" +#include "RuleManager.h" + +using namespace ctx; +using namespace ctx::trigger; + +Trigger::Trigger() : + __ruleMgr(NULL) +{ +} + +Trigger::~Trigger() +{ +} + +bool Trigger::init(ContextManager* ctxMgr) +{ + // Do the necessary initialization process. + // This function is called from the main thread during the service launching process. + _D("Context Trigger Init"); + __processInitialize(ctxMgr); + + return true; +} + +void Trigger::release() +{ + // Release the occupied resources. + // This function is called from the main thread during the service termination process. + + _D("Template Manager Destroy"); + TemplateManager::destroy(); + + _D("Rule Manager Release"); + delete __ruleMgr; + __ruleMgr = NULL; + + _D("Context Monitor Destroy"); + ContextMonitor::destroy(); +} + +bool Trigger::assignRequest(RequestInfo* request) +{ + std::string subject = request->getSubject(); + if (subject != SUBJ_TRIGGER_ADD && subject != SUBJ_TRIGGER_REMOVE && + subject != SUBJ_TRIGGER_ENABLE && subject != SUBJ_TRIGGER_DISABLE && + subject != SUBJ_TRIGGER_GET && subject != SUBJ_TRIGGER_GET_RULE_IDS && + subject != SUBJ_TRIGGER_GET_TEMPLATE) { + return false; + } + + __processRequest(request); + return true; +} + +void Trigger::__processRequest(RequestInfo* request) +{ + // Process the request, and reply to the client if necessary. + const char* reqSubj = request->getSubject(); + _D("Request is %s", reqSubj); + std::string subject(reqSubj); + + if (subject == SUBJ_TRIGGER_ADD) { + __addRule(request); + } else if (subject == SUBJ_TRIGGER_REMOVE) { + __removeRule(request); + } else if (subject == SUBJ_TRIGGER_ENABLE) { + __enableRule(request); + } else if (subject == SUBJ_TRIGGER_DISABLE) { + __disableRule(request); + } else if (subject == SUBJ_TRIGGER_GET) { + __getRuleById(request); + } else if (subject == SUBJ_TRIGGER_GET_RULE_IDS) { + __getRuleIds(request); + } else if (subject == SUBJ_TRIGGER_GET_TEMPLATE) { + __getTemplate(request); + } else { + _E("Invalid request"); + } + + delete request; +} + +void Trigger::__processInitialize(ContextManager* mgr) +{ + // Context Monitor + ContextMonitor::setContextManager(mgr); + + // Rule Manager + __ruleMgr = new(std::nothrow) RuleManager(); + IF_FAIL_VOID_TAG(__ruleMgr, _E, "Memory allocation failed"); + + // Template Manager + TemplateManager::setManager(mgr, __ruleMgr); + TemplateManager* tmplMgr = TemplateManager::getInstance(); + IF_FAIL_VOID_TAG(tmplMgr, _E, "Memory allocation failed"); + + // Initialization + if (!tmplMgr->init()) { + _E("Template manager initialization failed"); + raise(SIGTERM); + } + + if (!__ruleMgr->init()) { + _E("Context trigger initialization failed"); + raise(SIGTERM); + } +} + +void Trigger::__addRule(RequestInfo* request) +{ + Json ruleId; + + const char* client = request->getClient(); + if (client == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + const char* pkgId = request->getPackageId(); + + int error = __ruleMgr->addRule(client, pkgId, request->getDescription(), &ruleId); + _I("'%s' adds a rule (Error: %#x)", request->getClient(), error); + + request->reply(error, ruleId); +} + +void Trigger::__removeRule(RequestInfo* request) +{ + int id; + int error; + + const char* pkgId = request->getPackageId(); + + Json ruleId = request->getDescription(); + ruleId.get(NULL, TRIG_KEY_RULE_ID, &id); + + error = __ruleMgr->checkRule((pkgId)? pkgId : "", id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = __ruleMgr->isRuleEnabled(id); + if (ret) { + request->reply(ERR_RULE_ENABLED); + return; + } + + error = __ruleMgr->removeRule(id); + _I("'%s' removes rule%d (Error: %#x)", request->getClient(), id, error); + request->reply(error); +} + +void Trigger::__enableRule(RequestInfo* request) +{ + int id; + int error; + + const char* pkgId = request->getPackageId(); + + Json ruleId = request->getDescription(); + ruleId.get(NULL, TRIG_KEY_RULE_ID, &id); + + error = __ruleMgr->checkRule((pkgId)? pkgId : "", id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = __ruleMgr->isRuleEnabled(id); + if (ret) { + request->reply(ERR_RULE_ENABLED); + return; + } + + error = __ruleMgr->enableRule(id); + _I("'%s' enables rule%d (Error: %#x)", request->getClient(), id, error); + request->reply(error); +} + +void Trigger::__disableRule(RequestInfo* request) +{ + int id; + int error; + + const char* pkgId = request->getPackageId(); + + Json ruleId = request->getDescription(); + ruleId.get(NULL, TRIG_KEY_RULE_ID, &id); + + error = __ruleMgr->checkRule((pkgId)? pkgId : "", id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = __ruleMgr->isRuleEnabled(id); + if (!ret) { + request->reply(ERR_RULE_NOT_ENABLED); + return; + } + + error = __ruleMgr->disableRule(id); + _I("'%s' disables rule%d (Error: %#x)", request->getClient(), id, error); + request->reply(error); +} + +void Trigger::__getRuleById(RequestInfo* request) +{ + int error; + + Json option = request->getDescription(); + int id; + option.get(NULL, TRIG_KEY_RULE_ID, &id); + + const char* pkgId = request->getPackageId(); + + Json readData; + error = __ruleMgr->getRuleById((pkgId)? pkgId : "", id, &readData); + + Json dummy; + request->reply(error, dummy, readData); +} + +void Trigger::__getRuleIds(RequestInfo* request) +{ + int error; + + const char* pkgId = request->getPackageId(); + + Json readData; + error = __ruleMgr->getRuleIds((pkgId)? pkgId : "", &readData); + + Json dummy; + request->reply(error, dummy, readData); +} + +void Trigger::__getTemplate(RequestInfo* request) +{ + int error; + + Json option = request->getDescription(); + std::string name; + option.get(NULL, TRIG_TMPL_KEY_SUBJECT, &name); + + TemplateManager* tmplMgr = TemplateManager::getInstance(); + if (!tmplMgr) { + _E("Memory allocation failed"); + request->reply(ERR_OUT_OF_MEMORY); + return; + } + + Json tmpl; + error = tmplMgr->getTemplate(name, &tmpl); + + Json dummy; + request->reply(error, dummy, tmpl); +} diff --git a/src/agent/legacy/trigger/Trigger.h b/src/agent/legacy/trigger/Trigger.h new file mode 100644 index 0000000..161c99e --- /dev/null +++ b/src/agent/legacy/trigger/Trigger.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CONTEXT_TRIGGER_TRIGGER_H_ +#define _CONTEXT_TRIGGER_TRIGGER_H_ + +#include "../Request.h" + +namespace ctx { + + class client_request; + class ContextManager; + +namespace trigger { + + class RuleManager; + + class Trigger { + public: + Trigger(); + ~Trigger(); + + bool init(ContextManager* ctxMgr); + void release(); + + bool assignRequest(RequestInfo* request); + + private: + void __processRequest(RequestInfo* request); + void __processInitialize(ContextManager* mgr); + + void __addRule(RequestInfo* request); + void __removeRule(RequestInfo* request); + void __enableRule(RequestInfo* request); + void __disableRule(RequestInfo* request); + void __getRuleById(RequestInfo* request); + void __getRuleIds(RequestInfo* request); + void __getTemplate(RequestInfo* request); + + RuleManager* __ruleMgr; + }; + +} /* namespace trigger */ +} /* namespace ctx */ + +#endif /* End of _CONTEXT_TRIGGER_TRIGGER_H_ */ -- 2.34.1