Implement logic 10/122210/17
authorZofia Abramowska <z.abramowska@samsung.com>
Mon, 3 Apr 2017 17:51:22 +0000 (19:51 +0200)
committerRafal Krypa <r.krypa@samsung.com>
Mon, 3 Apr 2017 18:53:11 +0000 (20:53 +0200)
Implement whole logic of new askuser-notification.

Change-Id: I9d8f2df3530c1607020ee058028617e3c6f92800

src/agent/notification-daemon/CMakeLists.txt
src/agent/notification-daemon/Logic.cpp [new file with mode: 0644]
src/agent/notification-daemon/Logic.h [new file with mode: 0644]

index 83b671f..30ce27b 100644 (file)
@@ -25,12 +25,14 @@ INCLUDE_DIRECTORIES(
     ${ASKUSER_PATH}
     ${ASKUSER_PATH}/common
     ${ASKUSER_PATH}/common/protocol
+    ${ASKUSER_PATH}/agent/notification-daemon
 )
 
 SET(ASKUSER_NOTIFICATION_SOURCES
     ${NOTIF_PATH}/main.cpp
     ${NOTIF_PATH}/GuiRunner.cpp
     ${NOTIF_PATH}/AskUserTalker.cpp
+    ${NOTIF_PATH}/Logic.cpp
     ${NOTIF_PATH}/ServerCallbacks.cpp
     ${NOTIF_PATH}/ui/Po.cpp
     ${ASKUSER_PATH}/common/log/alog.cpp
diff --git a/src/agent/notification-daemon/Logic.cpp b/src/agent/notification-daemon/Logic.cpp
new file mode 100644 (file)
index 0000000..272201e
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co.
+ *
+ *  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
+ */
+/**
+ * @file        src/agent/notification-daemon/Service.cpp
+ * @author      Zofia Abramowska <z.abramowska@samsung.com>
+ * @brief       Declaration of Popupper class
+ */
+
+#include "Logic.h"
+
+#include <algorithm>
+#include <signal.h>
+#include <string>
+#include <sys/signalfd.h>
+#include <vconf.h>
+#include <Elementary.h>
+
+#include <exception/Exception.h>
+#include <exception/ErrnoException.h>
+#include "ServerCallbacks.h"
+
+namespace AskUser {
+
+namespace Notification {
+
+void Logic::addChannelFd(int fd, Ecore_Fd_Handler_Flags flags) {
+    auto it = m_fdInfo.find(fd);
+    if (it != m_fdInfo.end()) {
+        m_fdInfo[fd].status = FdChange::CHANGE;
+        ecore_main_fd_handler_del(m_fdInfo[fd].handler);
+    }
+
+    auto handler = ecore_main_fd_handler_add(fd, flags, &Logic::clientChHandler, this, nullptr, nullptr);
+    if (handler == nullptr) {
+        ALOGE("Couldn't set fd handler");
+        return;
+    }
+    m_fdInfo[fd].handler = handler;
+    m_fdInfo[fd].status = FdChange::NONE;
+}
+
+void Logic::removeChannelFd(int fd) {
+    auto it = m_fdInfo.find(fd);
+    if (it == m_fdInfo.end()) {
+        return;
+    }
+    it->second.status = FdChange::DEL;
+    ecore_main_fd_handler_del(it->second.handler);
+}
+
+Eina_Bool Logic::clientChHandler(void *data, Ecore_Fd_Handler *handler) {
+    Logic *service = static_cast<Logic *>(data);
+    int fd = ecore_main_fd_handler_fd_get(handler);
+    if (fd == -1) {
+        ALOGE("Failed to fetch fd from handler");
+        return ECORE_CALLBACK_CANCEL;
+    }
+    int mask = 0;
+    if (ecore_main_fd_handler_active_get(handler, ECORE_FD_READ)) {
+        mask = AskUser::Protocol::READ;
+    } else if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE)) {
+        mask = AskUser::Protocol::WRITE;
+    }
+    return service->processChannel(fd, mask);
+}
+
+Eina_Bool Logic::processChannel(int fd, int mask) {
+    m_fdInfo[fd].status = FdChange::NONE;
+    updateChannel(fd, mask);
+
+    switch (m_fdInfo[fd].status) {
+    case FdChange::NONE:
+        return ECORE_CALLBACK_RENEW;
+    case FdChange::DEL: {
+        // Remove all pending events from this fd
+        auto queueIt = std::remove_if(m_pendingEvents.begin(), m_pendingEvents.end(), [fd](const FdEvent &fdEvent) {return fdEvent.fd == fd;});
+        m_pendingEvents.erase(queueIt, m_pendingEvents.end());
+
+        // Handle next event if removed active fd
+        if (fd == m_currentFd) {
+            finishCurrentEvent();
+            processEvents();
+        }
+        m_fdInfo.erase(fd);
+        return ECORE_CALLBACK_CANCEL;
+    }
+    case FdChange::CHANGE:
+        return ECORE_CALLBACK_CANCEL;
+    default:
+        ALOGE("Unexpected fd change value : " << static_cast<int>(m_fdInfo[fd].status));
+        return ECORE_CALLBACK_CANCEL;
+    }
+}
+
+void Logic::addEvent(IUIEvent *event) {
+    FdEvent fdEvent{m_currentFd, std::unique_ptr<IUIEvent>(event)};
+    m_pendingEvents.emplace_back(std::move(fdEvent));
+    processEvents();
+}
+
+bool Logic::isEventProcessed() {
+    return m_currentEventId != -1;
+}
+
+void Logic::finishCurrentEvent() {
+    m_popupper.popupClose(m_currentEventId);
+    m_currentEventId = -1;
+    m_currentFd = -1;
+}
+
+void Logic::processEvents() {
+    // Active event processing
+    if (isEventProcessed())
+        return;
+
+    // No pending events
+    if (m_pendingEvents.empty())
+        return;
+
+    FdEvent fdEvent = std::move(m_pendingEvents.front());
+    m_currentFd = fdEvent.fd;
+    m_currentEventId = fdEvent.event->getId();
+    m_pendingEvents.pop_front();
+
+    fdEvent.event->process();
+}
+
+void Logic::registerSignalFd() {
+    // TODO use ecore event EXIT?
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGTERM);
+
+    if (pthread_sigmask(SIG_BLOCK, &mask, NULL) < 0) {
+        throw ErrnoException("Couldn't set signal mask");
+    }
+
+    int signalFd = signalfd(-1, &mask, 0);
+    if (signalFd < 0) {
+        throw ErrnoException("Couldn't create signalfd");
+    }
+    auto handler = ecore_main_fd_handler_add(signalFd, ECORE_FD_READ, &Logic::signalHandler, this, nullptr, nullptr);
+    if (handler == nullptr) {
+        throw Exception("Couldn't set fd handler");
+    }
+}
+
+void Logic::stop() {
+    if (isEventProcessed())
+        finishCurrentEvent();
+    m_popupper.stop();
+}
+
+Eina_Bool Logic::signalHandler(void *data, Ecore_Fd_Handler *) {
+    Logic *logic = static_cast<Logic*>(data);
+    logic->stop();
+    return ECORE_CALLBACK_CANCEL;
+}
+
+void Logic::prepareChannel() {
+    m_clientChannel = AskUser::Protocol::createChannel(
+            std::unique_ptr<AskUser::Protocol::IServerCallbacks>(new AskUser::Protocol::ServerCallbacks(this, &m_popupper)));
+}
+
+void Logic::updateChannel(int fd, int mask) {
+    // TODO errors?
+    m_clientChannel->process(fd, mask);
+}
+
+void Logic::init() {
+    init_agent_log();
+    m_popupper.setLocale();
+    m_popupper.initialize();
+    m_popupper.registerPopupResponseHandler([&](int popupId, NResponseType response) { popupResponse(popupId, response);});
+    m_popupper.registerToastFinishedHandler([&](int toastId) {toastFinished(toastId);});
+
+    registerSignalFd();
+
+    prepareChannel();
+}
+
+void Logic::run() {
+    // TODO ensure we won't throw inside ecore loop
+    m_popupper.start();
+    m_popupper.shutdown();
+}
+
+void Logic::popupResponse(int popupId, NResponseType response) {
+    if (popupId != m_currentEventId) {
+        ALOGD("Got different popup id than is being processed");
+        return;
+    }
+    int clientResponse;
+    // TODO translate ui response to policy result
+    switch (response) {
+    case NResponseType::Deny:
+        clientResponse = 0;
+        break;
+    case NResponseType::DenyAlways:
+        clientResponse = 1;
+        break;
+    case NResponseType::Allow:
+        clientResponse = 2;
+        break;
+    case NResponseType::AllowAlways:
+        clientResponse = 3;
+        break;
+    case NResponseType::None:
+        clientResponse = 0;
+        break;
+    case NResponseType::Error:
+        clientResponse = -255;
+        break;
+    default:
+        clientResponse = -255; // error
+    }
+    m_clientChannel->popupResponse(popupId, clientResponse);
+    finishCurrentEvent();
+    processEvents();
+}
+
+void Logic::toastFinished(int toastId) {
+    if (toastId != m_currentEventId) {
+        ALOGD("Got different toast id than is being processed");
+        return;
+    }
+    finishCurrentEvent();
+    processEvents();
+}
+
+} // namespace Notification
+
+} // namepsace AskUser
diff --git a/src/agent/notification-daemon/Logic.h b/src/agent/notification-daemon/Logic.h
new file mode 100644 (file)
index 0000000..95fe8bc
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co.
+ *
+ *  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
+ */
+/**
+ * @file        src/agent/notification-daemon/Service.h
+ * @author      Zofia Abramowska <z.abramowska@samsung.com>
+ * @brief       Declaration of Popupper class
+ */
+
+#pragma once
+
+#include <map>
+#include <deque>
+#include <askuser-notification/ask-user-service.h>
+#include <event/Event.h>
+
+namespace AskUser {
+
+namespace Notification {
+
+class Logic {
+public:
+    class Exception : public std::exception {
+    public:
+        Exception(std::string &&s) : m_msg(std::move(s)) {}
+        const char *what() const throw () {
+            return m_msg.c_str();
+        }
+    private:
+        std::string m_msg;
+    };
+    Logic() : m_currentFd(-1), m_currentEventId(-1), m_isFdDeleted(false) {}
+    void init();
+    void run();
+    void stop();
+
+    void addChannelFd(int fd, Ecore_Fd_Handler_Flags flags);
+    void removeChannelFd(int fd);
+
+    void addEvent(IUIEvent *event);
+
+    ~Logic() {}
+private:
+    //Initialization
+    void registerSignalFd();
+    void prepareChannel();
+    void updateChannel(int fd, int mask);
+
+    // Descriptor handlers
+    static Eina_Bool clientChHandler(void *data, Ecore_Fd_Handler *handler);
+    static Eina_Bool signalHandler(void *data, Ecore_Fd_Handler *handler);
+    //static Eina_Bool signalHandler(void *data, int type, void *event);
+
+    Eina_Bool processChannel(int fd, int mask);
+    void processEvents();
+    bool isEventProcessed();
+    void finishCurrentEvent();
+    void popupResponse(int popupId, NResponseType response);
+    void toastFinished(int toastId);
+
+    AskUser::Protocol::ChannelPtr m_clientChannel;
+    Popupper m_popupper;
+
+    struct FdEvent {
+        int fd;
+        std::unique_ptr<IUIEvent> event;
+    };
+
+    // Workaround for no information about to which fd event belongs to
+    int m_currentFd;
+    int m_currentEventId;
+    int m_isFdDeleted;
+    std::deque<FdEvent> m_pendingEvents;
+
+    enum class FdChange {
+        NONE,
+        ADD,
+        CHANGE,
+        DEL
+    };
+    struct FdInfo {
+        Ecore_Fd_Handler* handler;
+        FdChange status;
+    };
+
+    std::map<int, FdInfo> m_fdInfo;
+};
+
+} // namespace Notification
+
+} // namespace AskUser