Adjust server & client logic to handle many privileges in input 80/179780/31
authorTomasz Swierczek <t.swierczek@samsung.com>
Tue, 22 May 2018 07:28:33 +0000 (09:28 +0200)
committerTomasz Swierczek <t.swierczek@samsung.com>
Tue, 28 Aug 2018 04:56:58 +0000 (04:56 +0000)
This commit is a preparation for new API with many privileges as input.

Change-Id: I9964bbdb1fa81211bdd0117e48a5462604407d17
Signed-off-by: Ernest Borowski <e.borowski@partner.samsung.com>
src/capi/impl/privacy_privilege_manager.c
src/client/api/ApiInterface.h
src/client/api/askuser-notification-client.cpp
src/client/impl/ApiInterfaceImpl.cpp
src/client/impl/ApiInterfaceImpl.h
src/client/impl/ClientCallbacks.cpp
src/client/include/askuser-notification-client.h
src/notification-daemon/Logic.cpp
src/notification-daemon/Logic.h
src/notification-daemon/ServerCallbacks.cpp

index 7545252..a99ea09 100644 (file)
@@ -18,6 +18,7 @@
  * @file        privacy_privilege_manager.c
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
  * @author      Ernest Borowski <e.borowski@partner.samsung.com>
+ * @author      Tomasz Swierczek <t.swierczek@samsung.com>
  * @brief       The implementation of Privacy Privilege Manager CAPI.
  */
 
@@ -48,6 +49,12 @@ struct ppm_callback_closure_s {
 };
 typedef struct ppm_callback_closure_s ppm_callback_closure;
 
+struct ppm_multiple_callback_closure_s {
+    void *user_data;
+    ppm_request_multiple_response_cb callback;
+};
+typedef struct ppm_multiple_callback_closure_s ppm_multiple_callback_closure;
+
 static ppm_private *ppm_handle = NULL;
 
 static void ppm_free_channel(gpointer data)
@@ -220,6 +227,41 @@ static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_caus
     free(callback_closure);
 }
 
+static void ppm_popup_multiple_response_callback(UNUSED int request_id, askuser_call_cause cause,
+                                        const askuser_popup_result *results, const char **privileges,
+                                        size_t privileges_count, void *p_user_data)
+{
+    ppm_multiple_callback_closure *callback_closure = (ppm_multiple_callback_closure *) p_user_data;
+
+    /* Don't invoke callback while the application is finishing. The user data
+     * may already have been destroyed.
+     */
+    if (cause == ASKUSER_CALL_CAUSE_FINALIZE) {
+        free(callback_closure);
+        return;
+    }
+
+    ppm_call_cause_e ppm_cause = askuser_client_request_cause_to_ppm(cause);
+
+    ppm_request_result_e *ppm_results = malloc(privileges_count * sizeof(ppm_request_result_e));
+
+    if (!ppm_results) {
+        callback_closure->callback(ASKUSER_CALL_CAUSE_ERROR, NULL, privileges, privileges_count, callback_closure->user_data);
+        free(callback_closure);
+        return;
+    }
+
+    for (size_t iterator = 0; iterator < privileges_count; iterator++) {
+        ppm_results[iterator] = askuser_client_request_result_to_ppm(results[iterator]);
+    }
+
+    callback_closure->callback(ppm_cause, ppm_results, privileges, privileges_count, callback_closure->user_data);
+
+    free(ppm_results);
+    free(callback_closure);
+}
+
+
 static void ppm_free_client()
 {
     if (ppm_handle != NULL) {
@@ -295,10 +337,37 @@ EXPORT_API
 int ppm_check_permissions(const char **privileges, size_t privileges_count,
                           ppm_check_result_e *results)
 {
-    (void)privileges;
-    (void)privileges_count;
-    (void)results;
-    return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
+    if (!privileges_count || !privileges || !results) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+    }
+    for (size_t iterator = 0; iterator < privileges_count; iterator++) {
+        if (!privileges[iterator]) {
+            return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+        }
+    }
+
+    int ret = ppm_init_client();
+    if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
+        return ret;
+    }
+
+    askuser_check_result *check_results = malloc(privileges_count * sizeof(askuser_check_result));
+    if (!check_results) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
+    }
+
+    ret = askuser_client_check_privileges(ppm_handle->client, privileges, privileges_count, check_results);
+    if (ret != ASKUSER_API_SUCCESS) {
+        free(check_results);
+        return ask_user_to_ppm_error(ret);
+    }
+
+    for (size_t iterator = 0; iterator < privileges_count; iterator++) {
+        results[iterator] = ask_user_check_result_to_ppm(check_results[iterator]);
+    }
+
+    free(check_results);
+    return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
 }
 
 EXPORT_API
@@ -336,9 +405,31 @@ int ppm_request_permissions(const char **privileges, size_t privileges_count,
                             ppm_request_multiple_response_cb callback,
                             void *user_data)
 {
-    (void)privileges;
-    (void)privileges_count;
-    (void)callback;
-    (void)user_data;
-    return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
+    if (!privileges_count || !privileges || !callback) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+    }
+
+    int ret = ppm_init_client();
+    if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
+        return ret;
+    }
+
+    ppm_multiple_callback_closure *callback_closure = (ppm_multiple_callback_closure *)
+                                                      calloc(1, sizeof(ppm_multiple_callback_closure));
+    if(callback_closure == NULL) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
+    }
+
+    callback_closure->callback = callback;
+    callback_closure->user_data = user_data;
+
+    ret = askuser_client_popup_multiple_request(ppm_handle->client, privileges, privileges_count,
+                                                ppm_popup_multiple_response_callback,
+                                                callback_closure, NULL);
+    if (ret != ASKUSER_API_SUCCESS) {
+        free(callback_closure);
+        return ask_user_to_ppm_error(ret);
+    }
+
+    return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
 }
index c1b5d2c..2f6fd80 100644 (file)
 /**
  * @file        ApiInterface.h
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author      Ernest Borowski <e.borowski@partner.samsung.com>
  * @brief       This file contains the declaration of ApiInterface.
  */
 
 #pragma once
 
 #include <string>
+#include <vector>
 
 #include <askuser-notification-client.h>
 
@@ -42,6 +44,9 @@ public:
     virtual RequestId popupRequest(const std::string &privilege,
                                    const askuser_popup_response_callback callback,
                                    void *userData) = 0;
+    virtual RequestId popupRequest(const std::vector<std::string> &privileges,
+                                   const askuser_popup_multiple_response_callback callback,
+                                   void *userData) = 0;
     virtual bool popupRequestInProgress(const std::string &privilege) const = 0;
 };
 
index 1067816..5da69fa 100644 (file)
 /**
  * @file        askuser-notification-client.cpp
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author      Ernest Borowski <e.borowski@partner.samsung.com>
  * @brief       This file contains the implementation of the askuser-notification client API.
  */
 
 #include <cstring>
 #include <memory>
+#include <unordered_set>
+#include <vector>
 
 #include <log/alog.h>
 #include <askuser-notification-client.h>
 #include <attributes/attributes.h>
+#include <message-utils.h>
 
 #include <ApiInterface.h>
 #include <ApiInterfaceImpl.h>
@@ -111,6 +115,24 @@ int askuser_client_check_privilege(askuser_client *p_client,
 }
 
 API
+int askuser_client_check_privileges(askuser_client *p_client, const char **privileges,
+                                    size_t privileges_count, askuser_check_result *p_results)
+{
+    if (!p_client || !privileges || !p_results ||
+        privileges_count == 0 || privileges_count > AskUser::Protocol::MAX_PRIVS_NUMBER) {
+        return ASKUSER_API_INVALID_PARAM;
+    }
+
+    int ret = ASKUSER_API_SUCCESS;
+    for (size_t i = 0; i < privileges_count; ++i) {
+        ret = askuser_client_check_privilege(p_client, privileges[i], &(p_results[i]));
+        if (ret != ASKUSER_API_SUCCESS)
+            break;
+    }
+    return ret;
+}
+
+API
 int askuser_client_popup_request(askuser_client *p_client, const char *privilege,
                                  askuser_popup_response_callback response_callback,
                                  void *p_user_data, int *p_request_id)
@@ -137,3 +159,41 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege
         return ASKUSER_API_SUCCESS;
     });
 }
+
+API
+int askuser_client_popup_multiple_request(askuser_client *p_client, const char **privileges,
+                                          size_t privileges_count,
+                                          askuser_popup_multiple_response_callback response_callback,
+                                          void *p_user_data, int *p_request_id)
+{
+    if (!privileges_count || !p_client || !privileges || !response_callback) {
+        return ASKUSER_API_INVALID_PARAM;
+    }
+
+    return AskUser::Client::tryCatch([&]() {
+        std::vector<std::string> privilegeVector;
+        for (size_t it = 0 ; it < privileges_count; it++) {
+            if (!privileges[it]  || std::strlen(privileges[it]) == 0) {
+                return ASKUSER_API_INVALID_PARAM;
+            }
+            if (p_client->impl->popupRequestInProgress(privileges[it])) {
+                return ASKUSER_API_ALREADY_IN_PROGRESS;
+            }
+            privilegeVector.push_back(privileges[it]);
+        }
+
+        std::unordered_set<std::string> uniquePrivileges(privilegeVector.begin(), privilegeVector.end());
+        if (privileges_count != uniquePrivileges.size()) {
+            ALOGE("Privileges passed to multiple request are not unique.");
+            return ASKUSER_API_INVALID_PARAM;
+        }
+        AskUser::Client::RequestId id = p_client->impl->popupRequest(privilegeVector, response_callback, p_user_data);
+
+        if (p_request_id) {
+            *p_request_id = id;
+        }
+
+        return ASKUSER_API_SUCCESS;
+    });
+
+}
index f7972ef..5b6ed44 100644 (file)
@@ -25,6 +25,7 @@
 #include <algorithm>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sstream>
 
 #include <log/alog.h>
 #include <policy/Policy.h>
@@ -63,12 +64,13 @@ inline askuser_popup_result responseToAskUserPopupResult(int response)
     return ASKUSER_POPUP_RESULT_DENY_ONCE;
 }
 
-inline askuser_call_cause deduceCauseFromResponse(int response)
+inline askuser_call_cause deduceCauseFromResponses(const std::vector<int> &responses)
 {
-    if (response == ASKUSER_UNKNOWN_ERROR) {
-        return ASKUSER_CALL_CAUSE_ERROR;
+    for (const auto response : responses) {
+        if (response == ASKUSER_UNKNOWN_ERROR) {
+            return ASKUSER_CALL_CAUSE_ERROR;
+        }
     }
-
     return ASKUSER_CALL_CAUSE_ANSWER;
 }
 
@@ -125,8 +127,23 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege,
                                          const askuser_popup_response_callback callback,
                                          void *userData)
 {
-    std::vector<std::string> privileges({privilege});
-    Protocol::ConnectionContext conCtx = m_channel->popupRequest(std::move(privileges));
+    return popupRequestInternal({privilege}, NULL, callback, userData);
+}
+
+RequestId ApiInterfaceImpl::popupRequest(const std::vector<std::string> &privileges,
+                                         const askuser_popup_multiple_response_callback callback,
+                                         void *userData)
+{
+    return popupRequestInternal(privileges, callback, NULL, userData);
+}
+
+
+RequestId ApiInterfaceImpl::popupRequestInternal(const std::vector<std::string> &privileges,
+                                                 const askuser_popup_multiple_response_callback multipleCallback,
+                                                 const askuser_popup_response_callback singleCallback,
+                                                 void *userData)
+{
+    Protocol::ConnectionContext conCtx = m_channel->popupRequest(privileges);
 
     auto sameRequest = [&] (const Request &req) {
         return req.m_requestId == conCtx.m_requestId;
@@ -135,15 +152,12 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege,
     auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameRequest);
     if (reqIt != m_requests.end()) {
         ALOGE("Popup closure exists for id: " << conCtx.m_requestId <<
-              " privilege: " << reqIt->m_privilege << ", replacing");
-        popupResponse(conCtx.m_requestId, ASKUSER_UNKNOWN_ERROR);
+              " Request: " << *reqIt << ", replacing");
+        std::vector<int> responses(reqIt->m_privileges.size(), ASKUSER_UNKNOWN_ERROR);
+        popupResponses(conCtx.m_requestId, responses);
     }
 
-    if (popupRequestInProgress(privilege)) {
-        ALOGE("Privilege " << privilege << " already exists in the pending requests");
-    }
-
-    m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privilege, callback, userData });
+    m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privileges, multipleCallback, singleCallback, userData });
 
     return conCtx.m_requestId;
 }
@@ -151,7 +165,10 @@ RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege,
 bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const
 {
     auto samePrivilege = [&] (const Request &req) {
-        return req.m_privilege == privilege;
+        auto result = std::find(req.m_privileges.begin(), req.m_privileges.end(), privilege);
+        if (result != req.m_privileges.end())
+            return true;
+        return false;
     };
 
     return std::find_if(m_requests.begin(), m_requests.end(), samePrivilege) != m_requests.end();
@@ -170,16 +187,53 @@ void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask)
         if (reqIt == m_requests.end()) {
             return;
         }
-
         ALOGW("askuser-notification has been unexpectedly stopped, "
-              "sending the error response for privilege: "
-              << reqIt->m_privilege << " id: " << reqIt->m_requestId);
+              "sending the error response for request: " << *reqIt);
+
+        std::vector<int> responses(reqIt->m_privileges.size(), ASKUSER_UNKNOWN_ERROR);
 
-        popupResponse(reqIt->m_requestId, ASKUSER_UNKNOWN_ERROR);
+        popupResponses(reqIt->m_requestId, responses);
     }
 }
 
-void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response)
+void ApiInterfaceImpl::popupResponsesHelper(Protocol::RequestId id, const Request &request,
+                                            askuser_call_cause cause, const std::vector<int> &responses)
+{
+    if (request.m_singleCallback) {
+        if (responses.empty()) {
+            ALOGE("Responses are empty");
+            request.m_singleCallback(id, ASKUSER_CALL_CAUSE_ERROR,
+                                     responseToAskUserPopupResult(ASKUSER_DENY_ONCE), NULL, request.m_userData);
+            return;
+        }
+        if (responses.size() > 1) {
+            ALOGE("Responses count: " + std::to_string(responses.size()) + " should be 1.");
+        }
+        request.m_singleCallback(id, cause, responseToAskUserPopupResult(responses[0]),
+                                 request.m_privileges[0].c_str(), request.m_userData);
+        return;
+    }
+
+    size_t dataSize = responses.size();
+    if (dataSize != request.m_privileges.size()) {
+        ALOGE("Size mismatched: responses.size() " << responses.size() << " != privileges.size() "
+                << request.m_privileges.size());
+        request.m_multipleCallback(id, ASKUSER_CALL_CAUSE_ERROR, NULL, NULL, 0, request.m_userData);
+        return;
+    }
+    std::vector<const char *> privileges;
+    privileges.reserve(dataSize);
+    std::transform(request.m_privileges.begin(), request.m_privileges.end(), std::back_inserter(privileges),
+                    [](const std::string &priv) -> const char* { return priv.c_str(); });
+
+    std::vector<askuser_popup_result> results;
+    results.reserve(dataSize);
+    std::transform(responses.begin(), responses.end(), std::back_inserter(results),
+            [](int response) -> askuser_popup_result { return responseToAskUserPopupResult(response); });
+
+    request.m_multipleCallback(id, cause, results.data(), privileges.data(), dataSize, request.m_userData);
+}
+void ApiInterfaceImpl::popupResponses(Protocol::RequestId id, const std::vector<int> &responses)
 {
     auto sameRequestId = [&] (const Request &req) {
         return req.m_requestId == id;
@@ -191,19 +245,16 @@ void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response)
         return;
     }
 
-    askuser_call_cause cause = deduceCauseFromResponse(response);
-    askuser_popup_result res = responseToAskUserPopupResult(response);
-
-    reqIt->m_callback(id, cause, res, reqIt->m_privilege.c_str(), reqIt->m_userData);
-
+    askuser_call_cause cause = deduceCauseFromResponses(responses);
+    popupResponsesHelper(id, *reqIt, cause, responses);
     m_requests.erase(reqIt);
 }
 
 void ApiInterfaceImpl::respondToAllRequests(askuser_call_cause cause, askuser_popup_result result)
 {
     for (const auto &req : m_requests) {
-        req.m_callback(req.m_requestId, cause, result,
-                       req.m_privilege.c_str(), req.m_userData);
+        std::vector<int> results(req.m_privileges.size(), result);
+        popupResponsesHelper(req.m_requestId, req, cause, results);
     }
 
     m_requests.clear();
index 34e5cee..1c829a4 100644 (file)
 /**
  * @file        ApiInterfaceImpl.h
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author      Ernest Borowski <e.borowski@partner.samsung.com>
  * @brief       The declaration of ApiInterfaceImpl.
  */
 
 #pragma once
 
+#include <iostream>
 #include <memory>
+#include <list>
+#include <string>
 #include <vector>
 
 #include <ApiInterface.h>
@@ -51,25 +55,45 @@ public:
     virtual RequestId popupRequest(const std::string &privilege,
                                    const askuser_popup_response_callback callback,
                                    void *userData);
+    virtual RequestId popupRequest(const std::vector<std::string> &privileges,
+                                   const askuser_popup_multiple_response_callback callback,
+                                   void *userData);
     virtual bool popupRequestInProgress(const std::string &privilege) const;
 
     void updateConnection(Protocol::ConnectionFd fd, int mask);
-    void popupResponse(Protocol::RequestId id, int response);
+    void popupResponses(Protocol::RequestId id, const std::vector<int> &responses);
 
 private:
+    RequestId popupRequestInternal(const std::vector<std::string> &privileges,
+                                   const askuser_popup_multiple_response_callback multipleCallback,
+                                   const askuser_popup_response_callback singleCallback,
+                                   void *userData);
+
     void respondToAllRequests(askuser_call_cause cause, askuser_popup_result result);
 
     struct Request {
         Protocol::RequestId m_requestId;
         Protocol::ConnectionFd m_fd;
-        std::string m_privilege;
-        askuser_popup_response_callback m_callback;
+        std::vector<std::string> m_privileges;
+        askuser_popup_multiple_response_callback m_multipleCallback;
+        askuser_popup_response_callback m_singleCallback;
         void *m_userData;
+
+        friend std::ostream& operator << (std::ostream &os, const Request &request) {
+            os << "ConnectionFd: " << request.m_fd << " RequestId: " << request.m_requestId <<
+                    " N: " << request.m_privileges.size() << " privileges: ";
+            for (const auto &privilege : request.m_privileges)
+                os << privilege << " ";
+            return os;
+        }
     };
 
+    void popupResponsesHelper(Protocol::RequestId id, const Request &request,
+                              askuser_call_cause cause, const std::vector<int> &responses);
+
     StatusCallbackClosure m_statusClosure;
     std::unique_ptr<Protocol::ClientChannel> m_channel;
-    std::vector<Request> m_requests;
+    std::list<Request> m_requests;
     AulAppInfo m_appInfo;
     AulPkgInfo m_pkgInfo;
 };
index 5ac1551..7d8f91f 100644 (file)
@@ -34,7 +34,7 @@ void ClientCallbacks::updateConnection(Protocol::ConnectionFd fd, int mask)
 
 void ClientCallbacks::popupResponses(Protocol::RequestId id, const std::vector<int> &responses)
 {
-    m_api->popupResponse(id, responses[0]);
+    m_api->popupResponses(id, responses);
 }
 
 } // namespace Client
index 8398fdd..8175516 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2017 - 2018 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,6 +17,7 @@
 /**
  * @file        askuser-notification-client.h
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @author      Ernest Borowski <e.borowski@partner.samsung.com>
  * @brief       The declaration of the askuser-notification client API.
  */
 
@@ -213,6 +214,33 @@ typedef void (*askuser_popup_response_callback) (int request_id,
                                                  void *p_user_data);
 
 /**
+ * \brief This callback function is called when a client receives a response
+ * upon calling askuser_client_popup_multiple_request().
+ *
+ * \attention This callback is called from the askuser_client_process() function context.
+ *
+ * \param[in]   request_id        A number identifying the request. It will be equal to
+ *                                the value of *p_request_id generated by a corresponding
+ *                                askuser_client_popup_multiple_request() function call.
+ * \param[in]   cause             A value representing the reason why this callback has
+ *                                been called.
+ * \param[in]   results           It is a results array of response to request created by
+ *                                askuser_client_popup_multiple_request(). This should be
+ *                                interpreted as a valid value only if cause is equal to
+ *                                askuser_call_cause::ASKUSER_CALL_CAUSE_ANSWER.
+ * \param[in]   privileges        A privileges array that has been checked.
+ * \param[in]   privileges_count  The number of elements of the privileges and results arrays.
+ * \param[in]   p_user_data       User specific data, this parameter was previously
+ *                                passed to askuser_client_popup_request().
+ */
+typedef void (*askuser_popup_multiple_response_callback) (int request_id,
+                                                          askuser_call_cause cause,
+                                                          const askuser_popup_result results[],
+                                                          const char **privileges,
+                                                          size_t privileges_count,
+                                                          void *p_user_data);
+
+/**
  * \brief Description
  * Initialize askuser-notification client. It allocates structure and
  * initializes it using a given status callback and user data.
@@ -367,6 +395,34 @@ int askuser_client_check_privilege(askuser_client *p_client, const char *privile
 
 /**
  * \brief Description
+ * This function is called to check if an application has access to given
+ * privileges.
+ *
+ * \par Purpose:
+ * This API function should be called if an application wants to check if it has
+ * access to given privileges.
+ *
+ * \par Sync (or) Async:
+ * This is a synchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe.
+ *
+ * \param[in]   p_client          An instance of the askuser_client structure previously
+ *                                created by askuser_client_initialize().
+ * \param[in]   privileges        Privileges array that is to be checked.
+ * \param[in]   privileges_count  The number of elements of the privileges and results arrays.
+ *                                The parameter value should not exceed 100.
+ * \param[out]  p_results         The results array of a privileges check.
+ *
+ * \return ASKUSER_API_SUCCESS on success
+ * \return a negative value in case of an error (see "Status return codes")
+ */
+int askuser_client_check_privileges(askuser_client *p_client, const char **privileges,
+                                    size_t privileges_count, askuser_check_result *p_results);
+
+/**
+ * \brief Description
  * This function is called to determine a privacy privilege .
  *
  * \par Purpose:
@@ -517,6 +573,55 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege
  * \endcode
  */
 
+ /**
+ * \brief Description
+ * This function is called to determine privacy privileges.
+ *
+ * \par Purpose:
+ * This API function should be called if an application wants to determine privacy
+ * privileges. When this function is called, the askuser-notification service shows
+ * an appropriate UI dialog box (popup) with questions about gaining access to a list of privileges.
+ * When the user makes a decision, the service modifies a security policy and sends the response
+ * back to the application.
+ * The application is informed about user's decision by calling
+ * askuser_popup_multiple_response_callback.
+ *
+ * \par Method of function operation:
+ * If the result of calling askuser_client_check_privileges() is
+ * askuser_check_result::ASKUSER_CHECK_RESULT_ASK, the application should call
+ * this function to determine whether a user allows an application to use
+ * privileges.
+ *
+ * \par Sync (or) Async:
+ * This is an asynchronous API.
+ *
+ * \par Thread-safety:
+ * This function is NOT thread-safe.
+ *
+ * \param[in]   p_client            An instance of the askuser_client structure
+ *                                  previously created by askuser_client_initialize().
+ * \param[in]   privileges          A privileges array for which a popup must be shown.
+ * \param[in]   privileges_count    The number of elements of the privileges and results arrays.
+ *                                  The parameter value should not exceed 100.
+ * \param[in]   response_callback   A callback function called when the API receives
+ *                                  a response.
+ * \param[in]   p_user_data         User specific data which will be passed to
+ *                                  the registered response_callback.
+ *                                  Set to NULL if you don't use user data.
+ * \param[out]  p_request_id        An identifier of a request generated by the API.
+ *                                  Set NULL if you don't use a request ID.
+ *
+ * \return ASKUSER_API_SUCCESS on success
+ * \return a negative value in case of an error (see "Status return codes")
+ */
+int askuser_client_popup_multiple_request(askuser_client *p_client, const char **privileges,
+                                          size_t privileges_count,
+                                          askuser_popup_multiple_response_callback response_callback,
+                                          void *p_user_data, int *p_request_id);
+
+//TODO: add sample code
+
+
 #ifdef __cplusplus
 }
 #endif
index 009a1e4..3e16add 100644 (file)
@@ -173,7 +173,7 @@ Eina_Bool Logic::clientChHandler(void *data, Ecore_Fd_Handler *handler) {
 }
 
 Eina_Bool Logic::processChannel(int fd, int mask) {
-    ALOGD("Porcesing fd " << fd << " with mask " << mask);
+    ALOGD("Processing fd " << fd << " with mask " << mask);
     updateChannel(fd, mask);
 
     switch (m_fdInfo[fd].status) {
@@ -188,7 +188,7 @@ Eina_Bool Logic::processChannel(int fd, int mask) {
         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], "Ask user");
+        (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);
@@ -208,11 +208,21 @@ Eina_Bool Logic::processChannel(int fd, int mask) {
     }
 }
 
-void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privacy> &privacies) {
+void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+                     const std::map<Privilege, std::vector<Privacy>> &privsToAskablePrivacies,
+                     const std::map<Privilege, Policy> &privsToPolicy,
+                     const std::vector<Privilege> &privs,
+                     const std::vector<Privacy> &privacies) {
     EventId eventId{fd, id};
-    PrivaciesSequence &seq = m_eventToPrivaciesSeq[eventId];
+    auto &ev = m_eventInfo[eventId];
+    PrivaciesSequence &seq = ev.privaciesSeq;
     seq.setPrivacies(privacies);
 
+    ev.privilegesAskablePrivacies = privsToAskablePrivacies;
+    ev.privilegesPolicy = privsToPolicy;
+    ev.privileges = privs;
+    ev.privacyResponses.clear();
+
     ConnectionInfo &conn = m_connToInfo[fd];
 
     Privacy currentPrivacy;
@@ -222,9 +232,11 @@ void Logic::addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id, const st
     m_pendingEvents.emplace_back(std::move(fdEvent));
 }
 
-void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::string &privilege) {
+void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privilege> &privileges) {
     try {
-        ALOGD("Request for privilege " << privilege << " from fd " << fd << " with id " << id);
+        ALOGD("Request from fd " << fd << " with id " << id << " for privileges N= " << privileges.size() << " :");
+        for (auto &privilege : privileges)
+            ALOGD("---- " << privilege);
 
         auto it = m_connToInfo.find(fd);
         if (it == m_connToInfo.end()) {
@@ -232,40 +244,56 @@ void Logic::popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::
             return;
         }
         ConnectionInfo &conn = it->second;
-
-        PrivilegePolicy privPolicy(conn.appId, privilege);
         PkgMgrAppInfo appInfo;
-        auto policyLevel = privPolicy.calculatePolicy(appInfo);
-
-        ALOGD("Privilege policy level calculated to : " << policyLevel);
-        if (policyLevel == "Allow") {
-            m_serverChannel->popupResponses(fd, id, {ASKUSER_ALLOW_FOREVER});
-            return;
-        }
-        if (policyLevel == "Deny") {
-            m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_FOREVER});
-            return;
-        }
-        if (policyLevel != "Ask user") {
-            ALOGE("Unknown policy set : " << policyLevel << " for (" << conn.appId << ", " << conn.user
-                  << ", " << privilege << ")");
-            m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_ONCE});
-            return;
+        std::map<Privilege, std::vector<Privacy>> privsToAskablePrivacies;
+        std::map<Privilege, Policy> privsToPolicy;
+        std::set<Privacy> uniquePrivacies;
+
+        for (auto &privilege : privileges) {
+            PrivilegePolicy privPolicy(conn.appId, privilege);
+            privsToPolicy[privilege] = privPolicy.calculatePolicy(appInfo);
+            ALOGD("Privilege " << privilege << " policy level calculated to : " << privsToPolicy[privilege]);
+            if (privsToPolicy[privilege] == "Ask user") {
+                privsToAskablePrivacies[privilege] = privPolicy.calculateAskablePrivacies(appInfo);
+                if (privsToAskablePrivacies[privilege].empty()) {
+                    ALOGE("All privacies for privilege " << privilege << " are already allowed");
+                    privsToPolicy[privilege] = "Allow";
+                } else {
+                    for (auto &privacy : privsToAskablePrivacies[privilege])
+                        uniquePrivacies.insert(privacy);
+                }
+
+            }
         }
 
-        auto privacies = privPolicy.calculateAskablePrivacies(appInfo);
-        if (privacies.empty()) {
-            ALOGE("All privacies for privilege " << privilege
-                  << " are already allowed");
-            m_serverChannel->popupResponses(fd, id, {ASKUSER_ALLOW_FOREVER});
+        if (uniquePrivacies.empty()) {
+            // nothing to ask about, we can already return answer
+            std::vector<int> responses;
+            responses.resize(privileges.size());
+            for (size_t i = 0; i < privileges.size(); ++i) {
+                if (privsToPolicy[privileges[i]] == "Allow") {
+                    responses[i] = ASKUSER_ALLOW_FOREVER;
+                } else if (privsToPolicy[privileges[i]] == "Deny") {
+                    responses[i] = ASKUSER_DENY_FOREVER;
+                } else {
+                    ALOGE("Unknown policy set : " << privsToPolicy[privileges[i]] << " for (" << conn.appId << ", " << conn.user
+                          << ", " << privileges[i] << ")");
+                    responses[i] = ASKUSER_DENY_ONCE;
+                }
+            }
+            m_serverChannel->popupResponses(fd, id, std::move(responses));
             return;
         }
 
-        addEvent(fd, id, privacies);
+        // we do have privacies to ask about, we need to generate event
+        std::vector<Privacy> privacies(uniquePrivacies.begin(), uniquePrivacies.end());
+        addEvent(fd, id, privsToAskablePrivacies, privsToPolicy, privileges, privacies);
         processEvents();
+
     } catch (const std::exception &e) {
         ALOGE("Failed to handle popup request : " << e.what());
-        m_serverChannel->popupResponses(fd, id, {ASKUSER_DENY_ONCE});
+        std::vector<int> responses(privileges.size(), ASKUSER_DENY_ONCE);
+        m_serverChannel->popupResponses(fd, id, std::move(responses));
     }
 }
 
@@ -279,7 +307,7 @@ bool Logic::isEventProcessed() {
 }
 
 void Logic::finishCurrentRequest() {
-    m_eventToPrivaciesSeq.erase(m_currentEvent);
+    m_eventInfo.erase(m_currentEvent);
     m_popupper.popupClose();
 
     m_currentEvent = EventId();
@@ -324,7 +352,7 @@ void Logic::registerSignalFd() {
 void Logic::stop() {
     if (isEventProcessed()) {
         ConnectionInfo &conn = m_connToInfo[m_currentEvent.fd];
-        processResponse(conn, ASKUSER_DENY_ONCE, "Ask user");
+        processResponse(conn); // TODO used to be ASKUSER_DENY_ONCE, "Ask user"
         finishCurrentRequest();
     }
     m_popupper.stop();
@@ -391,49 +419,51 @@ void Logic::run() {
     m_popupper.shutdown();
 }
 
-bool Logic::setPolicy(const ConnectionInfo &conn, const std::string &lastPolicy) {
-    ALOGD("Setting policy for app " << conn.appId << " with last policy " << lastPolicy);
-    std::string currentPrivacy;
-
-    PrivaciesSequence &privSeq = m_eventToPrivaciesSeq[m_currentEvent];
-    if (!privSeq.getCurrentPrivacy(currentPrivacy) && lastPolicy == "Allow") {
-        ALOGD("Privilege is allowed completely");
-    }
-
-    privSeq.rewind();
-
-    Privacy privacy;
-    while (privSeq.getNextPrivacy(privacy) && privacy != currentPrivacy) {
-        /*
-         * When privacy is not allowed with deny forever or deny once, then processing is stopped,
-         * so all privacies, which were already processed, are allowed.
-         */
-        if (!PolicyUpdater::update(conn.appId, privacy, "Allow")) {
-            // FIXME : Maybe ignore those errors and try to maintain the policy as consistent as we can?
-            ALOGE("Couldn't set policy for " << conn.appId << " privacy : " << privacy << " level : Allow");
+bool Logic::setPolicy(const ConnectionInfo &conn) {
+    ALOGD("Setting policy for app " << conn.appId);
+    for (auto &pr : m_eventInfo[m_currentEvent].privacyResponses) {
+        const auto &level = clientResponseToPolicy(pr.second);
+        if (!PolicyUpdater::update(conn.appId, pr.first, level)) {
+            ALOGE("Couldn't set policy for " << conn.appId << " privacy : " << pr.first << " level : " << level);
             return false;
         }
     }
-
-    if (!currentPrivacy.empty() && lastPolicy != "Ask user") {
-        /*
-         * setPolicy is called when privacy processing is either successfully finished or
-         * aborted due to error, so currentPrivacy will be empty in the first case and
-         * we don't set policy when user responds with "Deny once" (this translates to "Ask user")
-         */
-        if (!PolicyUpdater::update(conn.appId, currentPrivacy, lastPolicy)) {
-            ALOGE("Couldn't set policy for " <<  conn.appId << " privacy : " << privacy << " level : " << lastPolicy);
-        }
-    }
     return true;
 }
 
-void Logic::processResponse(const ConnectionInfo &conn, int clientResponse, const Policy &level) {
-    if (!setPolicy(conn, level)) {
-        m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, {ASKUSER_UNKNOWN_ERROR});
-    } else {
-        m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, {clientResponse});
+void Logic::processResponse(const ConnectionInfo &conn) {
+    auto &currentEvent = m_eventInfo[m_currentEvent];
+    std::vector<int> responses(currentEvent.privileges.size(), ASKUSER_UNKNOWN_ERROR);
+
+    if (setPolicy(conn)) {
+        for (size_t i = 0; i < responses.size(); ++i) {
+            const auto &privilege = currentEvent.privileges[i];
+            const auto &policy = currentEvent.privilegesPolicy[privilege];
+            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;
+                bool hasAnswer = false;
+                for (auto &privacy : currentEvent.privilegesAskablePrivacies[privilege]) {
+                    const auto &privResponses = currentEvent.privacyResponses;
+                    const auto privResponseIt = privResponses.find(privacy);
+                    if (privResponseIt != privResponses.end()) {
+                        responses[i] = std::min(privResponseIt->second, responses[i]);
+                        hasAnswer = true;
+                    }
+                }
+                if (!hasAnswer)
+                    responses[i] = ASKUSER_DENY_ONCE;
+            }
+        }
     }
+    m_serverChannel->popupResponses(m_currentEvent.fd, m_currentEvent.id, std::move(responses));
 }
 
 void Logic::popupResponse(NResponseType response) {
@@ -443,17 +473,24 @@ void Logic::popupResponse(NResponseType response) {
         return;
     }
     ConnectionInfo &conn = it->second;
-
+    PrivaciesSequence &seq = m_eventInfo[m_currentEvent].privaciesSeq;
     int clientResponse = uiResponseToClientResponse(response);
-    std::string level = clientResponseToPolicy(clientResponse);
-    if (clientResponse != ASKUSER_ALLOW_FOREVER) {
-        // Privacy not allowed, request is denied
-        processResponse(conn, clientResponse, level);
-        finishCurrentRequest();
 
+    std::string justAnsweredPrivacy;
+    if (!seq.getCurrentPrivacy(justAnsweredPrivacy)) {
+        ALOGE("Received response for unknown privacy - this should not happen");
+        finishCurrentRequest();
+        processEvents();
+        return;
+    }
+    m_eventInfo[m_currentEvent].privacyResponses[justAnsweredPrivacy] = clientResponse;
+    if (clientResponse != ASKUSER_ALLOW_FOREVER && m_eventInfo[m_currentEvent].privileges.size() == 1) {
+        // request for only 1 privilege is being processed and at least 1 privacy is not allowed
+        // stop iteration over privacies, its pointless as the privilege is alraedy dissallowed
+        processResponse(conn);
+        finishCurrentRequest();
     } else {
-        // Privacy allowed - check if more privacies are to process
-        PrivaciesSequence &seq = m_eventToPrivaciesSeq[m_currentEvent];
+        // request for many privileges is being processed or one privilege but still needs to be processed
         Privacy nextPrivacy;
         if (seq.getNextPrivacy(nextPrivacy)) {
             // More privacies to process - replace existing event with new privacy
@@ -462,8 +499,8 @@ void Logic::popupResponse(NResponseType response) {
             // don't call finishCurrentRequest here, because it will pop event, which we replaced
             m_currentEvent = EventId();
         } else {
-            // No more privacies, request is allowed
-            processResponse(conn, clientResponse, level);
+            // No more privacies, request is ready for response
+            processResponse(conn);
             finishCurrentRequest();
         }
     }
index 7360ae3..e05c405 100644 (file)
@@ -16,6 +16,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
  */
 
@@ -54,7 +55,7 @@ public:
     void updateChannelFd(Protocol::ConnectionFd fd, Ecore_Fd_Handler_Flags flags);
     void removeChannelFd(Protocol::ConnectionFd fd);
 
-    void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::string &privilege);
+    void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privilege> &privileges);
 
     ~Logic();
 private:
@@ -86,12 +87,16 @@ 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, const std::vector<Privacy> &privacies);
+    void addEvent(Protocol::ConnectionFd fd, Protocol::RequestId id,
+                  const std::map<Privilege, std::vector<Privacy>> &privsToAskablePrivacies,
+                  const std::map<Privilege, Policy> &privsToPolicy,
+                  const std::vector<Privilege> &privs,
+                  const std::vector<Privacy> &privacies);
     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, int clientResponse, const Policy &level);
+    void processResponse(const ConnectionInfo &conn);
     bool isEventProcessed();
     void finishCurrentRequest();
 
@@ -101,7 +106,7 @@ private:
     void popupResponse(NResponseType response);
 
     bool processError(EventId id, int error);
-    bool setPolicy(const ConnectionInfo &conn, const std::string &lastPolicy);
+    bool setPolicy(const ConnectionInfo &conn);
 
     std::unique_ptr<AskUser::Protocol::ServerChannel> m_serverChannel;
     Popupper m_popupper;
@@ -130,7 +135,15 @@ private:
 
     std::map<Protocol::ConnectionFd, ConnectionInfo> m_connToInfo;
 
-    std::map<EventId, PrivaciesSequence> m_eventToPrivaciesSeq;
+    struct EventInfo {
+        PrivaciesSequence privaciesSeq;
+        std::vector<Privilege> privileges;
+        std::map<Privilege, std::vector<Privacy>> privilegesAskablePrivacies;
+        std::map<Privilege, Policy> privilegesPolicy;
+        std::map<Privacy, int> privacyResponses;
+    };
+
+    std::map<EventId, EventInfo> m_eventInfo;
 
     Ecore_Timer *m_timer;
 };
index e830e84..dece9b2 100644 (file)
@@ -50,7 +50,7 @@ void ServerCallbacks::updateConnection(ConnectionFd fd, int mask) {
 }
 
 void ServerCallbacks::popup(ConnectionFd fd, RequestId id, std::vector<std::string> &&privileges) {
-    m_service->popup(fd, id, privileges[0]);
+    m_service->popup(fd, id, privileges);
 }
 
 } /* namespace Notification */