Integration with new askuser UI apps 80/226180/25
authorTomasz Swierczek <t.swierczek@samsung.com>
Thu, 27 Feb 2020 09:36:20 +0000 (10:36 +0100)
committerTomasz Swierczek <t.swierczek@samsung.com>
Mon, 9 Mar 2020 06:25:24 +0000 (07:25 +0100)
This patch adds implementation of ppm_popup_send_response API
as well as new implementation of requesting a popup, using
askuser-popup external application.

Internally the patch removes m_currentEvent usage, allowing
the popups to be processed, theoretically, in parallel
for many requests.

Patch does not:
* add security check to ppm_popup_send_response
* remove not-needed EFL code

Above things will be done in separate patches.

Change-Id: Iaeb1d797141b750b462d21bd619e92e0c647e235

26 files changed:
src/capi/impl/privacy_privilege_manager.c
src/capi/test/privacy_privilege_manager_test.cpp
src/client/api/ApiInterface.h
src/client/api/askuser-notification-client.cpp
src/client/impl/ApiInterfaceImpl.cpp
src/client/impl/ApiInterfaceImpl.h
src/client/include/askuser-notification-client.h
src/ipc/client-channel.cpp
src/ipc/client-channel.h
src/ipc/common-types.h
src/ipc/credentials.cpp
src/ipc/credentials.h
src/ipc/message-utils.h
src/ipc/server-channel.cpp
src/ipc/server-channel.h
src/ipc/test/main.cpp
src/notification-daemon/CMakeLists.txt
src/notification-daemon/Logic.cpp
src/notification-daemon/Logic.h
src/notification-daemon/PolicyUpdater.cpp
src/notification-daemon/PolicyUpdater.h
src/notification-daemon/ServerCallbacks.cpp
src/notification-daemon/ServerCallbacks.h
src/notification-daemon/event/Event.h
src/notification-daemon/ui/UIAppInvoker.cpp [new file with mode: 0644]
src/notification-daemon/ui/UIAppInvoker.h [new file with mode: 0644]

index 0aa75fa..49efb1f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -502,10 +502,41 @@ int ppm_popup_send_response(int popup_id,
                             ppm_popup_response_e *responses,
                             size_t privacies_count)
 {
-    (void) popup_id;
-    (void) privacies;
-    (void) responses;
-    (void) privacies_count;
+    if (!privacies || !responses || privacies_count < 1) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = ppm_init_client();
+    if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
+        return ret;
+    }
+
+    askuser_popup_result* aresults = (askuser_popup_result*) calloc(1, sizeof(askuser_popup_result) * privacies_count);
+    if (!aresults) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
+    }
+
+    for (size_t i = 0; i < privacies_count; ++i) {
+        switch( responses[i] ) {
+            case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESPONSE_ALLOW_FOREVER :
+                aresults[i] = ASKUSER_POPUP_RESULT_ALLOW_FOREVER;
+                break;
+            case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESPONSE_DENY_FOREVER :
+                aresults[i] = ASKUSER_POPUP_RESULT_DENY_FOREVER;
+                break;
+            case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESPONSE_DENY_ONCE :
+                aresults[i] = ASKUSER_POPUP_RESULT_DENY_ONCE;
+                break;
+            default:
+                free(aresults);
+                return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    ret = askuser_popup_send_ext_response(ppm_handle->client, popup_id,
+                            privacies, aresults, privacies_count);
+
+    free(aresults);
 
-    return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
+    return ret != ASKUSER_API_SUCCESS ? ask_user_to_ppm_error(ret) : PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
 }
index da16dfe..05a197a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -311,6 +311,13 @@ struct ServerSimulator : public IServerCallbacks {
         printPrompt(Mode::SERVER);
     }
 
+    virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+        // TODO
+        (void) popup_id;
+        (void) privacies;
+        (void) responses;
+    }
+
     void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) {
         auto &s = m_requests[fd];
 
index 25cd4f3..8d4c741 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ public:
     virtual RequestId popupRequest(const std::vector<std::string> &privileges,
                                    const askuser_popup_multiple_response_callback callback,
                                    void *userData) = 0;
+    virtual int popupResponseExt(int popup_id, const std::vector<std::string> &privacies, std::vector<int> &responses) = 0;
     virtual bool popupRequestInProgress(const std::string &privilege) const = 0;
 };
 
index 6377128..9f10b45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -236,3 +236,44 @@ int askuser_client_popup_multiple_request(askuser_client *p_client, const char *
     });
 
 }
+
+API
+int askuser_popup_send_ext_response(askuser_client *p_client, int popup_id,
+                            const char **privacies,
+                            const askuser_popup_result responses[],
+                            size_t privacies_count)
+{
+    if (!privacies || !responses || privacies_count < 1) {
+        return ASKUSER_API_INVALID_PARAM;
+    }
+
+    return AskUser::Client::tryCatch([&]() {
+        std::vector<std::string> vprivacies(privacies_count);
+        std::vector<int> vresponses(privacies_count);
+
+        for (size_t i = 0; i < privacies_count; ++i) {
+            switch(responses[i]) {
+            case ASKUSER_POPUP_RESULT_ALLOW_FOREVER :
+                vresponses[i] = ASKUSER_ALLOW_FOREVER;
+                break;
+            case ASKUSER_POPUP_RESULT_DENY_FOREVER :
+                vresponses[i] = ASKUSER_DENY_FOREVER;
+                break;
+            case ASKUSER_POPUP_RESULT_DENY_ONCE :
+                vresponses[i] = ASKUSER_DENY_ONCE;
+                break;
+            default:
+                return ASKUSER_API_INVALID_PARAM;
+            }
+            vprivacies[i] = privacies[i];
+        }
+
+
+        int ret = p_client->impl->popupResponseExt(popup_id, vprivacies, vresponses);
+
+        // TODO there is no privilege check on server side now
+        // TODO we should be able to return ASKUSER_API_PERMISSION_DENIED in case when
+        // caller doesn't have a privilege (ie. http://tizen.org/privilege/internal/default/platform)
+        return ret != 0 ? ASKUSER_API_CONNECTION_ERROR : ASKUSER_API_SUCCESS;
+    });
+}
index cf7d75b..fe59c56 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -173,6 +173,11 @@ RequestId ApiInterfaceImpl::popupRequestInternal(const std::vector<std::string>
     return conCtx.m_requestId;
 }
 
+int ApiInterfaceImpl::popupResponseExt(int popup_id, const std::vector<std::string> &privacies, std::vector<int> &responses)
+{
+    return m_channel->popupResponseExt(popup_id, privacies, responses);
+}
+
 bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const
 {
     auto samePrivilege = [&] (const Request &req) {
index d70d80c..2aa8ca5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -60,6 +60,7 @@ public:
     virtual RequestId popupRequest(const std::vector<std::string> &privileges,
                                    const askuser_popup_multiple_response_callback callback,
                                    void *userData);
+    virtual int popupResponseExt(int popup_id, const std::vector<std::string> &privacies, std::vector<int> &responses);
     virtual bool popupRequestInProgress(const std::string &privilege) const;
 
     void updateConnection(Protocol::ConnectionFd fd, int mask);
index 7d1b840..cddce18 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -687,6 +687,28 @@ int askuser_client_popup_multiple_request(askuser_client *p_client, const char *
 
 //TODO: add sample code
 
+/**
+ * \brief Allows external UI popup app to pass per-privacy answers to askuser daemon
+ *
+ * \since_tizen 6.0
+ *
+ * \privlevel platform
+ *
+ * \param[in]   p_client          An instance of the askuser_client structure
+ *                                previously created by askuser_client_initialize().
+ * \param[in]   popup_id          The ID of the popup assigned by askuser daemon when UI popup app
+ *                                was invoked.
+ * \param[in]   privacies         The privacies array for which user response has been acquired.
+ * \param[in]   responses         The responses array for corresponding privacies.
+ * \param[in]   privacies_count   The number of elements in the privacies and results arrays.
+ *
+ * \return ASKUSER_API_SUCCESS on success
+ * \return a negative value in case of an error (see "Status return codes")
+ */
+int askuser_popup_send_ext_response(askuser_client *p_client, int popup_id,
+                            const char **privacies,
+                            const askuser_popup_result responses[],
+                            size_t privacies_count);
 
 #ifdef __cplusplus
 }
index 4b5b4f2..232f43c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -82,6 +82,35 @@ Protocol::ConnectionContext ClientChannel::popupRequest(const std::vector<std::s
     return ConnectionContext{requestId, fd};
 }
 
+int ClientChannel::popupResponseExt(int popup_id, const std::vector<std::string> &privacies, std::vector<int> &responses)
+{
+    int fd = connect();
+
+    std::stringstream ss;
+
+    ss << MSGID_EXT_POPUP_RESPONSE << " " << popup_id << " " << privacies.size();
+
+    for (auto &p : privacies)
+        ss << " " << base64Encode(p);
+
+    for (auto &r : responses)
+        ss << " " << r;
+
+    ss << '\n';
+
+    std::string str = ss.str();
+
+    ALOGD("popupResponseExt: sending message: " << str);
+
+    std::copy(str.begin(), str.end(), std::back_inserter(m_sockets[fd].output));
+
+    int ret = process(fd, FdMask::WRITE);
+
+    closeConnection(fd);
+
+    return ret;
+}
+
 void ClientChannel::onAccept(int) {
 }
 
index cc63dce..3ec8cba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -68,6 +68,7 @@ public:
     virtual ~ClientChannel();
 
     virtual ConnectionContext popupRequest(const std::vector<std::string> &privileges);
+    virtual int popupResponseExt(int popup_id, const std::vector<std::string> &privacies, std::vector<int> &responses);
 
 private:
     virtual void onAccept(int fd);
index aecafec..c147984 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@ enum FdMask {
 
 typedef int ConnectionFd;
 typedef int RequestId;
+typedef int PopupId;
 typedef std::string Privilege;
 typedef std::vector<Privilege> PrivilegeVector;
 
@@ -56,6 +57,7 @@ const RequestId INVALID_ID = -1;
 const int MSGID_POPUP = 1;
 const int MSGID_TOAST1 = 2;
 const int MSGID_TOAST2 = 3;
+const int MSGID_EXT_POPUP_RESPONSE = 4;
 const int MSGID_POPUP_RESPONSE = 1;
 
 } // namespace Protocol
index 8e1b98b..337bf08 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -41,6 +41,18 @@ std::string getUIDFromSocket(int sockFd)
     return std::to_string(cr.uid);
 }
 
+pid_t getPIDFromSocket(int sockFd)
+{
+    struct ucred cr;
+    socklen_t len = sizeof(cr);
+
+    if (getsockopt(sockFd, SOL_SOCKET, SO_PEERCRED, &cr, &len) == -1) {
+        throw AskUser::Protocol::CredentialsException("Couldn't fetch credentials from a socket");
+    }
+
+    return cr.pid;
+}
+
 std::string getSmackLabelFromSocket(int sockFd)
 {
     char *label;
@@ -61,6 +73,7 @@ namespace Protocol {
 Credentials::Credentials(int sockFd)
 : label(getSmackLabelFromSocket(sockFd))
 , uid(getUIDFromSocket(sockFd))
+, pid(getPIDFromSocket(sockFd))
 {
 }
 
index cecb0cf..4cb92de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ struct Credentials {
 
     std::string label;
     std::string uid;
+    pid_t pid;
 };
 
 } // namespace Protocol
index a842433..00aeb32 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -76,6 +76,34 @@ const std::size_t MAX_RESPONSE_STATUS_LEN = maxNumberOfChars<int>();
 const std::size_t MIN_PRIVILEGE_ENC_LENGTH = lengthOfEncodedBase64(MIN_PRIVILEGE_LENGTH);
 const std::size_t MAX_PRIVILEGE_ENC_LENGTH = lengthOfEncodedBase64(MAX_PRIVILEGE_LENGTH);
 const std::size_t SEP_LEN = 1; // space or \n
+const std::size_t MIN_EXT_RESPONSE_POPUP_ID_LEN = 1;
+const std::size_t MAX_EXT_RESPONSE_POPUP_ID_LEN = maxNumberOfChars<int>();
+
+const std::size_t MIN_EXT_RESPONSE_MESSAGE_LENGHT = MIN_COMMAND_LENGTH +
+                                               SEP_LEN + // space
+                                               MIN_EXT_RESPONSE_POPUP_ID_LEN +
+                                               SEP_LEN + // space
+                                               MIN_PRIVS_NUMBER_LENGTH +
+                                               MIN_PRIVS_NUMBER * (
+                                               SEP_LEN + // space
+                                               MIN_PRIVILEGE_ENC_LENGTH + // assuming privacy can't be longer than privilege
+                                               SEP_LEN + // space
+                                               MIN_RESPONSE_STATUS_LEN) + // response
+                                               SEP_LEN; // new line
+
+const std::size_t MAX_EXT_RESPONSE_MESSAGE_LENGHT = MAX_COMMAND_LENGTH +
+                                               SEP_LEN + // space
+                                               MAX_EXT_RESPONSE_POPUP_ID_LEN +
+                                               SEP_LEN + // space
+                                               MAX_PRIVS_NUMBER_LENGTH +
+                                               MAX_PRIVS_NUMBER * (
+                                               SEP_LEN + // space
+                                               MAX_PRIVILEGE_ENC_LENGTH) + // assuming privacy can't be longer than privilege
+                                               SEP_LEN + // space
+                                               MAX_PRIVS_NUMBER * (
+                                               SEP_LEN + // space
+                                               MAX_RESPONSE_STATUS_LEN) +
+                                               SEP_LEN; // new line
 
 const std::size_t MIN_REQUEST_MESSAGE_LENGTH = MIN_COMMAND_LENGTH +
                                                SEP_LEN + // space
@@ -117,14 +145,18 @@ const std::size_t MAX_RESPONSE_MESSAGE_LENGTH = MAX_COMMAND_LENGTH +
                                                 MAX_RESPONSE_STATUS_LEN) +
                                                 SEP_LEN; // new line
 
-const std::size_t MIN_MESSAGE_LENGTH = std::min(MIN_REQUEST_MESSAGE_LENGTH, MIN_RESPONSE_MESSAGE_LENGTH);
-const std::size_t MAX_MESSAGE_LENGTH = std::max(MAX_REQUEST_MESSAGE_LENGTH, MAX_RESPONSE_MESSAGE_LENGTH);
+const std::size_t MIN_MESSAGE_LENGTH = std::min(MIN_EXT_RESPONSE_MESSAGE_LENGHT,std::min(MIN_REQUEST_MESSAGE_LENGTH, MIN_RESPONSE_MESSAGE_LENGTH));
+const std::size_t MAX_MESSAGE_LENGTH = std::max(MAX_EXT_RESPONSE_MESSAGE_LENGHT,std::max(MAX_REQUEST_MESSAGE_LENGTH, MAX_RESPONSE_MESSAGE_LENGTH));
 
 const unsigned int ASKUSER_MESSAGE_CMD_POS = 0;
 const unsigned int ASKUSER_MESSAGE_REQUESTID_POS = 1;
 const unsigned int ASKUSER_MESSAGE_PRIVILEGES_COUNT_POS = 2;
 const unsigned int ASKUSER_MESSAGE_FIRST_PRIVILEGE_POS = 3;
 const unsigned int ASKUSER_MESSAGE_MIN_MSG_PARAM_COUNT = 4; //cmd, requestId, privilegeCount, privilege
+const unsigned int ASKUSER_MESSAGE_MIN_EXT_RESPONSE_MSG_PARAM_COUNT = 5; //cmd, popup_id, privacyCount, privacy, response
+const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_POPUP_ID_POS = 1;
+const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_PRIV_COUNT_POS = 2;
+const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS = 3;
 
 std::string base64Encode(std::string input);
 std::string base64Decode(std::string input);
index 9c83931..e129716 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -125,6 +125,34 @@ int ServerChannel::onReceive(int fd, std::vector<std::string> &&message) {
 
             break;
         }
+    case MSGID_EXT_POPUP_RESPONSE:
+        {
+            // TODO privilege check with Cynara should be here if we want to add it (ie. http://tizen.org/privilege/internal/default/platform)
+            // TODO how to return with access denied in this case?
+            unsigned int privaciesCount;
+            int popup_id;
+            if (message.size() < ASKUSER_MESSAGE_MIN_EXT_RESPONSE_MSG_PARAM_COUNT) {
+                ALOGE("Inappropriate message size for MSGID_EXT_POPUP_RESPONSE command, size: " << message.size());
+                return -EINVAL;
+            }
+            popup_id = std::stoi(message[ASKUSER_MESSAGE_EXT_RESPONSE_POPUP_ID_POS]);
+            privaciesCount = std::stoul(message[ASKUSER_MESSAGE_EXT_RESPONSE_PRIV_COUNT_POS]);
+            if (message.size() != 2 * privaciesCount + ASKUSER_MESSAGE_MIN_EXT_RESPONSE_MSG_PARAM_COUNT - 2) {
+                ALOGE("Inappropriate message size for MSGID_EXT_POPUP_RESPONSE command, size: " << message.size());
+                return -EINVAL;
+            }
+            ALOGD("privaciesCount: " << privaciesCount);
+            std::vector<std::string> privacies(privaciesCount);
+            std::vector<int> responses(privaciesCount);
+
+            for (unsigned int i = 0; i < privaciesCount; ++i) {
+                privacies[i] = base64Decode(message[ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS + i]);
+                responses[i] = std::stoi(message[ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS + privaciesCount + i]);
+            }
+            m_callbacks->extResponse(popup_id, std::move(privacies), std::move(responses));
+
+            break;
+        }
     default :
         ALOGE("Server received unknown message, command: " << command);
         return -EINVAL;
index ca0b913..c6e492c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -68,6 +68,15 @@ struct IServerCallbacks {
      * \param[in] privileges Privileges for which permissions are asked for
      */
     virtual void popup(ConnectionFd fd, RequestId id, std::vector<std::string> &&privileges) = 0;
+
+    /**
+     * This function is called when external UI app retrived answer and askuser needs to answer to app
+     *
+     * \param[in] popup_id the ID (cookie) passed to UI app when requesting popup, identifies application request (its ConnectionFd and RequestId)
+     * \param[in] privacies the privacies for which the UI app is returning an answer (it can be a list smaller than originally requested)
+     * \param[in] responses the list of policy responses (Allow/Deny) for each privacy; vector size must be equal to privacies.size()
+     */
+    virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) = 0;
 };
 
 typedef std::unique_ptr<IServerCallbacks> ServerCallbacksPtr;
index 6f130a0..365acbb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -62,6 +62,13 @@ struct ServerCallbacks : public IServerCallbacks {
         }
     }
 
+    virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+        // TODO
+        (void) popup_id;
+        (void) privacies;
+        (void) responses;
+    }
+
     void setChannel(ServerChannel *ptr) {
         m_channel = ptr;
     }
index 77d8df2..83917c6 100644 (file)
@@ -13,6 +13,7 @@ PKG_CHECK_MODULES(ASKUSER_NOTIFICATION_DEP
     capi-ui-efl-util
     capi-system-info
     efl-extension
+    aul
 )
 
 INCLUDE_DIRECTORIES(SYSTEM
@@ -33,6 +34,7 @@ SET(ASKUSER_NOTIFICATION_SOURCES
     ${NOTIF_PATH}/ServerCallbacks.cpp
     ${NOTIF_PATH}/ui/Po.cpp
     ${NOTIF_PATH}/ui/Popupper.cpp
+    ${NOTIF_PATH}/ui/UIAppInvoker.cpp
    )
 
 ADD_EXECUTABLE(${TARGET_ASKUSER_NOTIFICATION} ${ASKUSER_NOTIFICATION_SOURCES})
index c2027ef..8f58f02 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017-2019 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
 #include <policy/Policy.h>
 #include <policy/PrivilegePolicy.h>
 #include <ui/Po.h>
+#include <ui/UIAppInvoker.h>
 
 #include "PolicyUpdater.h"
 #include "ServerCallbacks.h"
@@ -46,33 +47,6 @@ namespace {
 
 int ASKUSER_IDLE_TIMER_SEC = 4;
 
-int uiResponseToClientResponse(NResponseType response) {
-    int clientResponse;
-
-    switch (response) {
-    case NResponseType::Deny:
-        clientResponse = ASKUSER_DENY_ONCE;
-        break;
-    case NResponseType::DenyAlways:
-        clientResponse = ASKUSER_DENY_FOREVER;
-        break;
-    case NResponseType::Allow:
-    case NResponseType::AllowAlways:
-        clientResponse = ASKUSER_ALLOW_FOREVER;
-        break;
-    case NResponseType::None:
-        clientResponse = ASKUSER_NONE;
-        break;
-    case NResponseType::Error:
-        clientResponse = ASKUSER_UNKNOWN_ERROR;
-        break;
-    default:
-        clientResponse = ASKUSER_UNKNOWN_ERROR;
-    }
-
-    return clientResponse;
-}
-
 std::string clientResponseToPolicy(int clientResponse) {
     std::string level;
 
@@ -115,7 +89,7 @@ void Logic::addChannelFd(Protocol::ConnectionFd fd, const Protocol::Credentials
         ALOGD("Proper client connected");
         stopTimer();
 
-        ConnectionInfo connInfo{appId, pkgId, creds.uid, isHybrid};
+        ConnectionInfo connInfo{appId, pkgId, creds.uid, creds.pid, isHybrid};
         m_connToInfo.insert(it, std::make_pair(fd, connInfo));
     } catch (const std::exception &e) {
         ALOGE("Failed to add channel fd " << fd);
@@ -187,18 +161,13 @@ Eina_Bool Logic::processChannel(int fd, int mask) {
         auto queueIt = std::remove_if(m_pendingEvents.begin(), m_pendingEvents.end(),
                 [fd](const FdEvent &fdEvent) {return fdEvent.id.fd == fd;});
         m_pendingEvents.erase(queueIt, m_pendingEvents.end());
-
-        // Ignore error in policy setting, we can't do anything about it nor we can inform user about error
-        (void)setPolicy(m_connToInfo[fd]); // TODO: now it doesn't set "Ask user" again
         m_connToInfo.erase(fd);
         m_fdInfo.erase(fd);
         m_serverChannel->process(fd, 0);
 
         // Handle next event if removed active fd
-        if (fd == m_currentEvent.fd) {
-            finishCurrentRequest();
-            processEvents();
-        }
+        // TODO remove eventInfo like: m_eventInfo.erase(respEvent.id);
+
         return ECORE_CALLBACK_CANCEL;
     }
     case FdChange::CHANGE:
@@ -209,7 +178,12 @@ Eina_Bool Logic::processChannel(int fd, int mask) {
     }
 }
 
-void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+Protocol::PopupId Logic::genUniquePopupId() {
+    static int counter = 0;
+    return counter++;
+}
+
+void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, Protocol::PopupId popup_id,
                      const std::vector<Privacy> &privacies,
                      const std::vector<PrivilegeStruct> &privilegesStructure) {
     EventId eventId{fd, id};
@@ -221,7 +195,8 @@ void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
 
     ConnectionInfo &conn = m_connToInfo[fd];
 
-    FdEvent fdEvent{eventId, std::unique_ptr<IUIEvent>(new EventPopupCheck(&m_popupper, conn.pkgId, privacies))};
+    FdEvent fdEvent{eventId, popup_id, std::shared_ptr<IUIEvent>(new ExtUIEvent(popup_id, conn.pid, conn.pkgId, privacies))};
+    fdEvent.event->process();
     m_pendingEvents.emplace_back(std::move(fdEvent));
 }
 
@@ -280,11 +255,9 @@ void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::
             return;
         }
 
-        // we do have privacies to ask about, we need to generate event
+        int unique_id = genUniquePopupId();
         std::vector<Privacy> privacies(uniquePrivacies.begin(), uniquePrivacies.end());
-        addEvent(fd, id, privacies, privilegesStructure);
-        processEvents();
-
+        addEvent(fd, id, unique_id, privacies, privilegesStructure);
     } catch (const std::exception &e) {
         ALOGE("Failed to handle popup request : " << e.what());
         std::vector<int> responses(privileges.size(), ASKUSER_DENY_ONCE);
@@ -292,36 +265,132 @@ void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::
     }
 }
 
-Logic::~Logic()
+void Logic::extResponse(Protocol::PopupId popup_id, const std::vector<Privacy> &privacies, const std::vector<int> &responses)
 {
-    m_serverChannel.reset();
-}
+    ALOGD("Got response for popup_id : " << popup_id << " for privacies: ");
+    for (auto &p : privacies) {
+        ALOGD("---- privacy : " << p);
+    }
 
-bool Logic::isEventProcessed() {
-    return m_currentEvent.fd != Protocol::INVALID_FD;
-}
+    FdEvent respEvent;
+    size_t pendingEventsIndex = 0;
+    for (FdEvent &ev : m_pendingEvents) {
+        if (ev.popup_id == popup_id) {
+            respEvent = ev;
+            break;
+        }
+        ++pendingEventsIndex;
+    }
 
-void Logic::finishCurrentRequest() {
-    m_eventInfo.erase(m_currentEvent);
-    m_popupper.popupClose();
+    if (respEvent.popup_id == -1) {
+        ALOGE("Got response for nonexisting popup_id : " << popup_id);
+        return;
+    }
 
-    m_currentEvent = EventId();
-    if (!m_pendingEvents.empty())
-        m_pendingEvents.pop_front();
-}
+    auto it = m_eventInfo.find(respEvent.id);
 
-void Logic::processEvents() {
-    // Active event processing
-    if (isEventProcessed())
+    if (it == m_eventInfo.end()) {
+        ALOGE("Got response from strange event id ");
         return;
+    }
 
-    // No pending events
-    if (m_pendingEvents.empty())
+    EventInfo &eventInfo = it->second;
+
+    auto itc = m_connToInfo.find(respEvent.id.fd);
+
+    if (itc == m_connToInfo.end()) {
+        ALOGE("Got response from inactive fd " << respEvent.id.fd);
         return;
+    }
 
-    FdEvent &fdEvent = m_pendingEvents.front();
-    m_currentEvent = fdEvent.id;
-    fdEvent.event->process();
+    ConnectionInfo &conn = itc->second;
+
+    try {
+        if (privacies.size() != responses.size()) {
+            ALOGE("Got improper answer from UI app, privacies size doesn't match responses size ("
+                 << privacies.size() << " vs. "<< responses.size() << "); popup_id: " << popup_id);
+            throw Exception("Got improper answer from UI app");
+        }
+
+        std::map<Privacy, int> received;
+        unsigned int i = 0;
+        for (auto p : privacies) {
+            received[p] = responses[i++];
+
+            if (received[p] != ASKUSER_DENY_ONCE &&
+                received[p] != ASKUSER_DENY_FOREVER &&
+                received[p] != ASKUSER_ALLOW_FOREVER) {
+                ALOGE("Got improper answer from UI app, answers are improper for privacy: " << p << " answer: " << received[p]);
+                throw Exception("Got improper answer from UI app");
+            }
+        }
+
+        // fill missing responses (some privacies may have not received any answer)
+        // also, calculate final policy levels
+        std::map<Privacy, int> finalPrivacyResponses;
+        std::vector<std::string> levels(eventInfo.uniquePrivacies.size());
+        i = 0;
+
+        for (auto p : eventInfo.uniquePrivacies) {
+            finalPrivacyResponses[p] = (received.find(p) == received.end()) ? ASKUSER_UNKNOWN_ERROR : received[p];
+            levels[i] = clientResponseToPolicy(finalPrivacyResponses[p]);
+            ++i;
+        }
+
+        // set policy
+        ALOGD("Setting policy for app " << conn.appId);
+
+        if (!PolicyUpdater::update(conn.pkgId, conn.appId, conn.isHybrid, eventInfo.uniquePrivacies, levels)) {
+            ALOGE("Couldn't set policy for " << conn.appId);
+            throw Exception("Couldn't set policy");
+        }
+
+        std::vector<int> privResponses(eventInfo.privilegesStructure.size(), ASKUSER_UNKNOWN_ERROR);
+
+        for (i = 0; i < privResponses.size(); ++i) {
+            const auto &privilegeStruct = eventInfo.privilegesStructure[i];
+            const auto &policy = privilegeStruct.policy;
+            if (policy == "Allow") {
+                privResponses[i] = ASKUSER_ALLOW_FOREVER;
+            } else if (policy == "Deny") {
+                privResponses[i] = ASKUSER_DENY_FOREVER;
+            } else if (policy != "Ask user") {
+                privResponses[i] = ASKUSER_DENY_ONCE;
+            } else {
+                // Ask user policy - need to calcualte from existing responses to privacies
+                // but only privacies askable for this privilege should be used!
+                privResponses[i] = ASKUSER_ALLOW_FOREVER;
+                for (auto &privacy : privilegeStruct.askablePrivacies) {
+                    const auto &privResponseIt = finalPrivacyResponses.find(privacy);
+                    if (privResponseIt != finalPrivacyResponses.end()) {
+                        privResponses[i] = std::min(privResponseIt->second, privResponses[i]);
+                    }
+                    else {
+                        ALOGE("Unable to response for askable privacy: " << privacy);
+                        privResponses[i] = ASKUSER_DENY_ONCE;
+                        break;
+                    }
+                }
+            }
+        }
+
+        // answer to application
+        m_serverChannel->popupResponses(respEvent.id.fd, respEvent.id.id, std::move(privResponses));
+
+        // remove event & info information
+        m_eventInfo.erase(respEvent.id);
+        m_pendingEvents.erase(m_pendingEvents.begin() + pendingEventsIndex);
+
+    } catch(const std::exception &e) {
+        ALOGE("Failed to handle popup request : " << e.what());
+        std::vector<int> responses(eventInfo.privilegesStructure.size(), ASKUSER_DENY_ONCE);
+        m_serverChannel->popupResponses(respEvent.id.fd, respEvent.id.id, std::move(responses));
+    }
+}
+
+Logic::~Logic()
+{
+    m_serverChannel.reset();
 }
 
 void Logic::registerSignalFd() {
@@ -345,11 +414,6 @@ void Logic::registerSignalFd() {
 }
 
 void Logic::stop() {
-    if (isEventProcessed()) {
-        ConnectionInfo &conn = m_connToInfo[m_currentEvent.fd];
-        processResponse(conn); // TODO used to be ASKUSER_DENY_ONCE, "Ask user"
-        finishCurrentRequest();
-    }
     m_popupper.stop();
 }
 
@@ -401,7 +465,6 @@ void Logic::init() {
     init_agent_log();
     m_popupper.initialize();
     Po::setLocale();
-    m_popupper.registerPopupResponseHandler([&](std::vector<NResponseType> responses) { popupResponses(responses);});
 
     registerSignalFd();
 
@@ -409,94 +472,8 @@ void Logic::init() {
 }
 
 void Logic::run() {
-    // TODO ensure we won't throw inside ecore loop
-    m_popupper.start();
-    m_popupper.shutdown();
-}
-
-bool Logic::setPolicy(const ConnectionInfo &conn) {
-    ALOGD("Setting policy for app " << conn.appId);
-    auto &currentEvent = m_eventInfo[m_currentEvent];
-    std::vector<std::string> levels(currentEvent.uniquePrivacies.size());
-
-    for (size_t i = 0; i < levels.size(); ++i) {
-        const auto &privacy = currentEvent.uniquePrivacies[i];
-        levels[i] = clientResponseToPolicy(currentEvent.privacyResponses[privacy]);
-    }
-
-    if (!PolicyUpdater::update(conn.pkgId, conn.appId, conn.isHybrid, currentEvent.uniquePrivacies, levels)) {
-        ALOGE("Couldn't set policy for " << conn.appId);
-        return false;
-    }
-
-    return true;
-}
-
-void Logic::processResponse(const ConnectionInfo &conn) {
-    auto &currentEvent = m_eventInfo[m_currentEvent];
-    std::vector<int> responses(currentEvent.privilegesStructure.size(), ASKUSER_UNKNOWN_ERROR);
-
-    if (setPolicy(conn)) {
-        for (size_t i = 0; i < responses.size(); ++i) {
-            const auto &privilegeStruct = currentEvent.privilegesStructure[i];
-            const auto &policy = privilegeStruct.policy;
-            if (policy == "Allow") {
-                responses[i] = ASKUSER_ALLOW_FOREVER;
-            } else if (policy == "Deny") {
-                responses[i] = ASKUSER_DENY_FOREVER;
-            } else if (policy != "Ask user") {
-                responses[i] = ASKUSER_DENY_ONCE;
-            } else {
-                // Ask user policy - need to calcualte from existing responses to privacies
-                // but only privacies askable for this privilege should be used!
-                responses[i] = ASKUSER_ALLOW_FOREVER;
-                for (auto &privacy : privilegeStruct.askablePrivacies) {
-                    const auto &privResponseIt = currentEvent.privacyResponses.find(privacy);
-                    if (privResponseIt != currentEvent.privacyResponses.end()) {
-                        responses[i] = std::min(privResponseIt->second, responses[i]);
-                    }
-                    else {
-                        ALOGE("Unable to response for askable privacy: " << privacy);
-                        responses[i] = ASKUSER_DENY_ONCE;
-                        break;
-                    }
-                }
-            }
-        }
-    }
-    m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, std::move(responses));
-}
-
-void Logic::popupResponses(const std::vector<NResponseType> &responses) {
-    auto it = m_connToInfo.find(m_currentEvent.fd);
-
-    if (it == m_connToInfo.end()) {
-        ALOGE("Got response from inactive fd " << m_currentEvent.fd);
-        return;
-    }
-
-    ConnectionInfo &conn = it->second;
-    auto &currentEvent = m_eventInfo[m_currentEvent];
-
-    if (responses.size() != currentEvent.uniquePrivacies.size()) {
-        ALOGE("Got invalid number of responses: " << responses.size() << " but expected: "
-                << currentEvent.uniquePrivacies.size());
-        finishCurrentRequest();
-        processEvents();
-        return;
-    }
-
-    for (size_t i = 0; i < responses.size(); ++i) {
-        int clientResponse = uiResponseToClientResponse(responses[i]);
-        const auto &privacy = currentEvent.uniquePrivacies[i];
-        ALOGD("Client response for privacy " << privacy << " is " << clientResponse);
-        currentEvent.privacyResponses[privacy] = clientResponse;
-    }
-
-    // No more privacies, since all are answered by one popup - request is ready for response
-    processResponse(conn);
-    finishCurrentRequest();
-    processEvents();
+   m_popupper.start();
+   m_popupper.shutdown();
 }
 
 } // namespace Notification
index dd7243f..01bf193 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017-2019 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
  * @file        src/agent/notification-daemon/Service.h
  * @author      Zofia Abramowska <z.abramowska@samsung.com>
  * @author      Tomasz Swierczek <t.swierczek@samsung.com>
- * @brief       Declaration of Popupper class
+ * @brief       Declaration of Logic class
  */
 
 #pragma once
@@ -45,7 +45,7 @@ public:
     private:
         std::string m_msg;
     };
-    Logic() : m_currentEvent{-1, -1}, m_timer(nullptr) {}
+    Logic() : m_timer(nullptr) {}
     void init();
     void run();
     void stop();
@@ -56,6 +56,8 @@ public:
 
     void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privilege> &privileges);
 
+    void extResponse(int popup_id, const std::vector<Privacy> &privacies, const std::vector<int> &responses);
+
     ~Logic();
 private:
     struct EventId {
@@ -73,6 +75,7 @@ private:
         std::string appId;
         std::string pkgId;
         std::string user;
+        pid_t pid;
         bool isHybrid;
     };
 
@@ -82,6 +85,9 @@ private:
         std::vector<Privacy> askablePrivacies;
     };
 
+    //Generate popup ID (cookie) for external UI app
+    Protocol::PopupId genUniquePopupId();
+
     //Initialization
     void registerSignalFd();
     void prepareChannel();
@@ -93,34 +99,47 @@ private:
     static Eina_Bool timerHandler(void *data);
     //static Eina_Bool signalHandler(void *data, int type, void *event);
 
-    void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+    void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, Protocol::PopupId popup_id,
                   const std::vector<Privacy> &privacies,
                   const std::vector<PrivilegeStruct> &privilegesStructure);
     Eina_Bool processChannel(int fd, int mask);
     bool identifyClient(const std::string &label, std::string &appId, std::string &pkgId);
 
-    void processEvents();
-    void processResponse(const ConnectionInfo &conn);
-    bool isEventProcessed();
-    void finishCurrentRequest();
-
     void startTimer();
     void stopTimer();
 
-    void popupResponses(const std::vector<NResponseType> &response);
-
     bool processError(EventId id, int error);
-    bool setPolicy(const ConnectionInfo &conn);
 
     std::unique_ptr<AskUser::Protocol::ServerChannel> m_serverChannel;
     Popupper m_popupper;
 
     struct FdEvent {
         EventId id;
-        std::unique_ptr<IUIEvent> event;
+        Protocol::PopupId popup_id;
+        std::shared_ptr<IUIEvent> event;
+        FdEvent():
+            id{-1, -1},
+            popup_id (-1)
+            {}
+        FdEvent(EventId an_id, Protocol::PopupId an_popup_id, std::shared_ptr<IUIEvent> an_event):
+            id(an_id.fd, an_id.id),
+            popup_id(an_popup_id),
+            event(an_event)
+            {}
+        FdEvent(const FdEvent &other):
+            id(other.id.fd, other.id.id),
+            popup_id(other.popup_id),
+            event(other.event)
+            {}
+        FdEvent& operator=(const FdEvent &other) {
+            id.fd = other.id.fd;
+            id.id = other.id.id;
+            popup_id = other.popup_id;
+            event = other.event;
+            return *this;
+        }
     };
 
-    EventId m_currentEvent;
     std::deque<FdEvent> m_pendingEvents;
 
     enum class FdChange {
index 068c17f..28aa76f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
  *    you may not use this file except in compliance with the License.
@@ -108,6 +108,6 @@ bool PolicyUpdater::update(const std::string &pkgId, const std::string &appId, b
     return false;
 }
 
-} // namespace Agent
+} // namespace Notification
 
 } // namespace AskUser
index 15815fe..cc3d8ed 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 - 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2016-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
  *    you may not use this file except in compliance with the License.
@@ -35,6 +35,6 @@ namespace PolicyUpdater {
                 const std::vector<std::string> &levels);
 };
 
-} // namespace Agent
+} // namespace Notification
 
 } // namespace AskUser
index dece9b2..555ab57 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -53,5 +53,9 @@ void ServerCallbacks::popup(ConnectionFd fd, RequestId id, std::vector<std::stri
     m_service->popup(fd, id, privileges);
 }
 
+void ServerCallbacks::extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+    m_service->extResponse(popup_id, privacies, responses);
+}
+
 } /* namespace Notification */
 } /* namespace Askuser */
index d8f1c27..ed4d6bb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -40,8 +40,9 @@ public:
     virtual void newConnection(ConnectionFd fd, const Credentials &creds);
     virtual void updateConnection(ConnectionFd fd, int mask);
     virtual void popup(ConnectionFd fd, RequestId id, std::vector<std::string> &&privileges);
+    virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses);
 
-    virtual ~ServerCallbacks() {}
+    virtual ~ServerCallbacks() {};
 private:
     AskUser::Notification::Logic *m_service;
 };
index f0dbff0..79003e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 - 2018 Samsung Electronics Co.
+ *  Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -25,7 +25,9 @@
 #include <string>
 #include <vector>
 
+#include <exception/Exception.h>
 #include <ui/Popupper.h>
+#include <ui/UIAppInvoker.h>
 
 namespace AskUser {
 namespace Notification {
@@ -39,6 +41,25 @@ protected:
     Popupper *m_popupper;
 };
 
+class ExtUIEvent : public IUIEvent {
+public:
+    ExtUIEvent(int popup_id, pid_t caller_app_pid, std::string pkg_id, std::vector<std::string> privacies)
+        : IUIEvent(NULL),
+            m_popup_id(popup_id),
+            m_caller_app_pid(caller_app_pid),
+            m_pkg_id(pkg_id),
+            m_privacies(privacies)
+    {}
+    virtual void process() {
+        (void)UIAppInvoker::invoke(m_popup_id, m_caller_app_pid, m_pkg_id, m_privacies);
+    }
+private:
+    int m_popup_id;
+    pid_t m_caller_app_pid;
+    std::string m_pkg_id;
+    std::vector<std::string> m_privacies;
+};
+
 class EventPopupCheck : public IUIEvent {
 public:
     EventPopupCheck(Popupper *popupper, const std::string &pkgId, const std::vector<std::string> &privacies)
@@ -46,7 +67,8 @@ public:
     {}
 
     virtual void process() {
-        m_popupper->popupCheck(m_pkgId, m_privacies);
+        // TODO we don't want askuser popups now, just the external app UI
+        // m_popupper->popupCheck(m_pkgId, m_privacies);
     }
 private:
     std::string m_pkgId;
diff --git a/src/notification-daemon/ui/UIAppInvoker.cpp b/src/notification-daemon/ui/UIAppInvoker.cpp
new file mode 100644 (file)
index 0000000..b545d1f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/**
+ * @file        UIAppInvoker.cpp
+ * @author      Tomasz Swierczek <t.swierczek@samsung.com>
+ * @brief       This file implements API used to call external UI app for popup using AUL bundle API
+ */
+#include "UIAppInvoker.h"
+
+#include <log/alog.h>
+
+#include <aul.h>
+#include <aul_svc.h>
+
+#define PID_BUFFER_SIZE 64
+
+namespace AskUser {
+
+namespace Notification {
+
+bool UIAppInvoker::invoke(int popup_id, pid_t caller_app_pid, const std::string &pkg_id, const std::vector<std::string> &privacies) {
+    ALOGD("Launching popup app for popup_id: " << popup_id << ", pkg_id of requestor app: " << pkg_id);
+
+    char buf[PID_BUFFER_SIZE];
+    bundle *b = NULL;
+    char** privacies_c = NULL;
+    int ret = -1;
+    size_t i = 0;
+
+    b = bundle_create();
+
+    if (!b) {
+        ALOGE("Cannot allocate memory for bundle creation");
+        return false;
+    }
+
+    /* Sets the caller process ID - TODO: should it be the applications PID like it is now? */
+    snprintf(buf, sizeof(buf), "%d", caller_app_pid);
+    ret = bundle_add_str(b, AUL_K_CALLER_PID, buf);
+    if (ret < 0) {
+        ALOGE("bundle_add_str() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Sets the caller user ID - askuser notification works in user domain, has same UID as the apps have */
+    snprintf(buf, sizeof(buf), "%u", getuid());
+    ret = bundle_add_str(b, AUL_K_CALLER_UID, buf);
+    if (ret < 0) {
+        ALOGE("bundle_add_str() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Sets caller appid, assuming here we need pkg id */
+    snprintf(buf, sizeof(buf), "%s", pkg_id.c_str());
+    ret = bundle_add_str(b, AUL_K_CALLER_APPID, buf);
+    if (ret < 0) {
+        ALOGE("bundle_add_str() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Sets privacies to show */
+    privacies_c = new char*[privacies.size()];
+    if (!privacies_c) {
+        ALOGE("Cannot allocate memory for bundle creation");
+        ret = -1;
+        goto error_exit;
+    }
+
+    memset(privacies_c, 0, privacies.size());
+
+    for (auto p : privacies) {
+        if (!(privacies_c[i] = new char[p.size() + 1])){
+            ALOGE("Cannot allocate memory for bundle creation");
+            ret = -1;
+            goto error_exit;
+        }
+        strcpy(privacies_c[i], p.c_str());
+        ++i;
+    }
+
+    ret = bundle_add_str_array(b, "privacy_list", const_cast<const char**>(privacies_c), privacies.size());
+    if (ret < 0) {
+        ALOGE("bundle_add_str_array() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Add popup_id - here its int, but it can be arbitrary buffer */
+    ret = bundle_add_byte(b, "popup_id", &popup_id, sizeof(popup_id));
+    if (ret < 0) {
+        ALOGE("bundle_add_byte() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Sets App Group Launch Mode */
+    ret = aul_svc_set_launch_mode(b, "group");
+    if (ret < 0) {
+        ALOGE("aul_svc_set_launch_mode() failed. ret = " << ret);
+        goto error_exit;
+    }
+
+    /* Set Popup UI style */
+    ret = aul_svc_add_data(b, "launch_type", "single");
+    if (ret < 0) {
+        ALOGE("aul_svc_add_data() failed. ret = " << ret);
+        goto error_exit;
+    }
+    /* Launch askuser-popup */
+    ret = aul_forward_app("org.tizen.askuser-popup", b);
+    if (ret < 0)
+        ALOGE("aul_forward_app() failed. ret = " << ret);
+    else
+        ALOGD("Launched app pid " << ret);
+
+error_exit:
+    bundle_free(b);
+    if (privacies_c)
+        for (i = 0; i < privacies.size(); ++i)
+            delete [] privacies_c[i];
+    delete [] privacies_c;
+
+    return ret >= 0;
+}
+
+} // namespace Notification
+
+} // namespace AskUser
diff --git a/src/notification-daemon/ui/UIAppInvoker.h b/src/notification-daemon/ui/UIAppInvoker.h
new file mode 100644 (file)
index 0000000..ba83eaa
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/**
+ * @file        UIAppInvoker.h
+ * @author      Tomasz Swierczek <t.swierczek@samsung.com>
+ * @brief       This file defines API used to call external UI app for popup
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <unistd.h>
+#include <sys/types.h>
+
+namespace AskUser {
+
+namespace Notification {
+
+namespace UIAppInvoker {
+    bool invoke(int popup_id, pid_t caller_app_pid, const std::string &pkg_id, const std::vector<std::string> &privacies);
+} // UIAppInvoker
+
+} // namespace Notification
+
+} // namespace AskUser