/*
- * 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.
#define UNUSED __attribute__((unused))
-struct ppm_private_s {
- askuser_client *client;
+struct ppm_channel_s {
GIOChannel *channel;
GIOCondition condition;
guint watch_id;
};
+typedef struct ppm_channel_s ppm_channel;
+
+struct ppm_private_s {
+ askuser_client *client;
+ GHashTable *channels;
+};
typedef struct ppm_private_s ppm_private;
struct ppm_callback_closure_s {
static ppm_private *ppm_handle = NULL;
-static void ppm_private_init(ppm_private *handle)
+static void ppm_free_channel(gpointer data)
{
- handle->channel = NULL;
- handle->condition = 0;
- handle->watch_id = 0;
+ ppm_channel* channel = data;
+
+ if (channel) {
+ g_source_remove(channel->watch_id);
+ g_io_channel_unref(channel->channel);
+ g_free(channel);
+ }
}
static ppm_error_e ask_user_to_ppm_error(int ask_error)
{
- ppm_error_e ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
-
switch (ask_error) {
case ASKUSER_API_SUCCESS:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
- break;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
case ASKUSER_API_UNKNOWN_ERROR:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
- break;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
case ASKUSER_API_OUT_OF_MEMORY:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
- break;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
case ASKUSER_API_INVALID_PARAM:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
- break;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
case ASKUSER_API_CONNECTION_ERROR:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR;
- break;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR;
case ASKUSER_API_ALREADY_IN_PROGRESS:
- ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS;
default:
break;
}
- return ret;
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
}
static ppm_check_result_e ask_user_check_result_to_ppm(askuser_check_result result)
{
switch (result) {
case ASKUSER_POPUP_RESULT_ALLOW_FOREVER:
- return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER;
+ return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER;
case ASKUSER_POPUP_RESULT_DENY_FOREVER:
return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_FOREVER;
case ASKUSER_POPUP_RESULT_DENY_ONCE:
return !!(cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL));
}
-static gboolean ppm_is_connected(ppm_private *handle)
-{
- return handle->channel != NULL;
-}
-
static gboolean ppm_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
{
int fd, events;
GIOCondition gio_condition = askuser_events_to_g_io_condition(events);
if (events == ASKUSER_EMPTY_EVENTS) {
- if (ppm_is_connected(handle)) {
- g_source_remove(handle->watch_id);
- g_io_channel_unref(handle->channel);
- ppm_private_init(handle);
- }
+ g_hash_table_remove(handle->channels, GINT_TO_POINTER(fd));
return;
}
- if (!ppm_is_connected(handle)) {
- handle->condition = gio_condition;
- handle->channel = g_io_channel_unix_new(fd);
+ ppm_channel *channel = g_hash_table_lookup(handle->channels, GINT_TO_POINTER(fd));
+
+ if (channel == NULL) {
+ channel = (ppm_channel *) g_malloc0(sizeof(ppm_channel));
+ if (!channel) {
+ return;
+ }
+
+ channel->condition = gio_condition;
+ channel->channel = g_io_channel_unix_new(fd);
- g_io_channel_set_encoding (handle->channel, NULL, NULL);
- g_io_channel_set_close_on_unref(handle->channel, FALSE);
+ g_io_channel_set_encoding (channel->channel, NULL, NULL);
+ g_io_channel_set_close_on_unref(channel->channel, FALSE);
- handle->watch_id = g_io_add_watch(handle->channel,
- handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ channel->watch_id = g_io_add_watch(channel->channel,
+ channel->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
ppm_gio_cb,
handle);
+
+ g_hash_table_insert(handle->channels, GINT_TO_POINTER(fd), (gpointer) channel);
return;
}
- if (handle->condition != gio_condition) {
- handle->condition = gio_condition;
- g_source_remove(handle->watch_id);
- handle->watch_id = g_io_add_watch(handle->channel,
- handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ if (channel->condition != gio_condition) {
+ channel->condition = gio_condition;
+ g_source_remove(channel->watch_id);
+ channel->watch_id = g_io_add_watch(channel->channel,
+ channel->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
ppm_gio_cb,
handle);
}
static void ppm_free_client()
{
if (ppm_handle != NULL) {
- askuser_client_finalize(ppm_handle->client);
+ if (ppm_handle->client != NULL) {
+ askuser_client_finalize(ppm_handle->client);
+ }
+
+ if (ppm_handle->channels != NULL) {
+ g_hash_table_destroy(ppm_handle->channels);
+ }
+
free(ppm_handle);
ppm_handle = NULL;
}
return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
}
- ppm_private_init(ppm_handle);
+ ppm_handle->channels = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, ppm_free_channel);
+
+ if (ppm_handle->channels == NULL) {
+ ppm_free_client();
+ return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
+ }
int ret = askuser_client_initialize(&ppm_handle->client, ask_status_callback, ppm_handle);
if (ret != ASKUSER_API_SUCCESS) {
- free(ppm_handle);
- ppm_handle = NULL;
+ ppm_free_client();
return ask_user_to_ppm_error(ret);
}
ret = atexit(ppm_free_client);
if (ret != 0) {
- askuser_client_finalize(ppm_handle->client);
- free(ppm_handle);
- ppm_handle = NULL;
+ ppm_free_client();
return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
}
}
/*
- * 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.
#include <string>
-#include <PopupCallbackClosure.h>
#include <askuser-notification-client.h>
namespace AskUser {
virtual int process(int fd, int events) = 0;
virtual askuser_check_result checkPrivilege(const std::string &privilege) = 0;
- virtual RequestId popupRequest(const PopupCallbackClosure &closure,
- const std::string &privilege) = 0;
+ virtual RequestId popupRequest(const std::string &privilege,
+ const askuser_popup_response_callback callback,
+ void *userData) = 0;
virtual bool popupRequestInProgress(const std::string &privilege) const = 0;
};
/*
- * 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.
#include <ApiInterface.h>
#include <ApiInterfaceImpl.h>
-#include <PopupCallbackClosure.h>
#include <StatusCallbackClosure.h>
#include <TryCatch.h>
return ASKUSER_API_ALREADY_IN_PROGRESS;
}
- AskUser::Client::PopupCallbackClosure closure(response_callback, privilege, p_user_data);
- AskUser::Client::RequestId id = p_client->impl->popupRequest(closure, privilege);
+ AskUser::Client::RequestId id = p_client->impl->popupRequest(privilege, response_callback, p_user_data);
if (p_request_id) {
*p_request_id = id;
/*
- * 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.
* @brief The definition of ApiInterfaceImpl.
*/
+#include <algorithm>
#include <unistd.h>
#include <sys/types.h>
return ASKUSER_CHECK_RESULT_DENY;
}
-RequestId ApiInterfaceImpl::popupRequest(const PopupCallbackClosure &closure,
- const std::string &privilege)
+RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege,
+ const askuser_popup_response_callback callback,
+ void *userData)
{
- RequestId id = static_cast<RequestId>(m_channel->popupRequest(privilege));
+ Protocol::ConnectionContext conCtx = m_channel->popupRequest(privilege);
- auto it = m_popupClosures.find(id);
- if (it != m_popupClosures.end()) {
- ALOGE("Popup closure exists for id: " << id <<
- " privilege: " << it->second.privilege() << ", replacing");
- popupResponse(id, ASKUSER_UNKNOWN_ERROR);
+ auto sameRequest = [&] (const Request &req) {
+ return req.m_requestId == conCtx.m_requestId;
+ };
+
+ 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);
}
if (popupRequestInProgress(privilege)) {
- ALOGE("Privilege " << closure.privilege() << " already exists in the pending privileges set");
+ ALOGE("Privilege " << privilege << " already exists in the pending requests");
}
- m_requestedPrivileges.insert(privilege);
- m_popupClosures.insert({id, closure});
+ m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privilege, callback, userData });
- return id;
+ return conCtx.m_requestId;
}
bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const
{
- return m_requestedPrivileges.find(privilege) != m_requestedPrivileges.end();
+ auto samePrivilege = [&] (const Request &req) {
+ return req.m_privilege == privilege;
+ };
+
+ return std::find_if(m_requests.begin(), m_requests.end(), samePrivilege) != m_requests.end();
}
void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask)
{
m_statusClosure(fd, askUserMaskToEvents(mask));
- // the connection is about to close, respond to all pending requests
if (mask == ASKUSER_EMPTY_EVENTS) {
- respondToAllRequests(ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE);
+ auto sameFd = [&] (const Request &req) {
+ return req.m_fd == fd;
+ };
+
+ auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameFd);
+ 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);
+
+ popupResponse(reqIt->m_requestId, ASKUSER_UNKNOWN_ERROR);
}
}
void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response)
{
- auto it = m_popupClosures.find(id);
- if (it == m_popupClosures.end()) {
- ALOGE("Couldn't find popup callback closure for id: " << id);
- return;
- }
+ auto sameRequestId = [&] (const Request &req) {
+ return req.m_requestId == id;
+ };
- const auto &closure = it->second;
- if (!popupRequestInProgress(closure.privilege())) {
- ALOGE("Couldn't find privilege " << closure.privilege() << " in the pending privileges set");
+ auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameRequestId);
+ if (reqIt == m_requests.end()) {
+ ALOGE("Couldn't find request for id: " << id);
+ return;
}
askuser_call_cause cause = deduceCauseFromResponse(response);
askuser_popup_result res = responseToAskUserPopupResult(response);
- closure(id, cause, res);
+ reqIt->m_callback(id, cause, res, reqIt->m_privilege.c_str(), reqIt->m_userData);
- m_requestedPrivileges.erase(closure.privilege());
- m_popupClosures.erase(it);
+ m_requests.erase(reqIt);
}
void ApiInterfaceImpl::respondToAllRequests(askuser_call_cause cause, askuser_popup_result result)
{
- for (const auto &closure : m_popupClosures) {
- closure.second(closure.first, cause, result);
+ for (const auto &req : m_requests) {
+ req.m_callback(req.m_requestId, cause, result,
+ req.m_privilege.c_str(), req.m_userData);
}
- m_requestedPrivileges.clear();
- m_popupClosures.clear();
+ m_requests.clear();
}
} // namespace Client
/*
- * 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.
#pragma once
-#include <map>
#include <memory>
-#include <set>
+#include <vector>
#include <ApiInterface.h>
-#include <PopupCallbackClosure.h>
#include <StatusCallbackClosure.h>
#include <client-channel.h>
virtual int process(int fd, int events);
virtual askuser_check_result checkPrivilege(const std::string &privilege);
- virtual RequestId popupRequest(const PopupCallbackClosure &closure,
- const std::string &privilege);
+ virtual RequestId popupRequest(const std::string &privilege,
+ const askuser_popup_response_callback callback,
+ void *userData);
virtual bool popupRequestInProgress(const std::string &privilege) const;
void updateConnection(Protocol::ConnectionFd fd, int mask);
private:
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;
+ void *m_userData;
+ };
+
StatusCallbackClosure m_statusClosure;
std::unique_ptr<Protocol::ClientChannel> m_channel;
- std::map<RequestId, PopupCallbackClosure> m_popupClosures;
- std::set<std::string> m_requestedPrivileges;
+ std::vector<Request> m_requests;
};
} // namespace Client
+++ /dev/null
-/*
- * Copyright (c) 2017 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 PopupCallbackClosure.h
- * @author Piotr Sawicki <p.sawicki2@partner.samsung.com>
- * @brief The definition of PopupCallbackClosure.
- */
-
-#pragma once
-
-#include <string>
-
-#include <askuser-notification-client.h>
-
-namespace AskUser {
-
-namespace Client {
-
-class PopupCallbackClosure {
-public:
- PopupCallbackClosure(askuser_popup_response_callback callback, const char *privilege, void *userData)
- : m_callback(callback)
- , m_privilege(privilege)
- , m_userData(userData)
- {}
-
- void operator()(int requestId, askuser_call_cause cause, askuser_popup_result result) const
- {
- m_callback(requestId, cause, result, m_privilege.c_str(), m_userData);
- }
-
- const std::string &privilege() const {
- return m_privilege;
- }
-
-private:
- askuser_popup_response_callback m_callback;
- std::string m_privilege;
- void *m_userData;
-};
-
-} // namespace Client
-
-} // namespace AskUser
-
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017 - 2018 Samsung Electronics Co.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
-void ClientChannel::init() {
+int ClientChannel::connect() {
Sock s(Sock::CLI_STREAM);
if (0 > s.connect(getStreamSocketPath(geteuid())))
throw ConnectionException("Cannot connect to the server");
int fd = s.getFd();
m_sockets[fd] = SockDesc(std::move(s));
+ return fd;
}
AskUser::Protocol::RequestId ClientChannel::generateRequestId() {
return requestId++;
}
-Protocol::RequestId ClientChannel::popupRequest(const std::string &privilege) {
- if (m_sockets.empty()) {
- init(); // client uses only one socket
- }
+Protocol::ConnectionContext ClientChannel::popupRequest(const std::string &privilege) {
+ int fd = connect(); // clients use only one connection per request
std::stringstream ss;
RequestId requestId = generateRequestId();
ss << MSGID_POPUP << " " << base64Encode(privilege) << " " << requestId << '\n';
- int fd = m_sockets.begin()->first;
std::string str = ss.str();
std::copy(str.begin(), str.end(), std::back_inserter(m_sockets[fd].output));
m_callbacks->updateConnection(fd, FdMask::READ | FdMask::WRITE);
- return requestId;
+ return ConnectionContext{requestId, fd};
}
void ClientChannel::onAccept(int) {
m_callbacks->updateConnection(fd, 0);
}
-int ClientChannel::onReceive(UNUSED int fd, std::vector<std::string> &&message) {
+int ClientChannel::onReceive(int fd, std::vector<std::string> &&message) {
if (!message.size()) {
ALOGE("Server received empty message");
return -EINVAL;
int response = std::stoi(message[2]);
m_callbacks->popupResponse(id, response);
+
+ // after receiving the response just close the connection
+ closeConnection(fd);
+
break;
}
default :
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017 - 2018 Samsung Electronics Co.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ClientChannel(ClientCallbacksPtr ptr);
virtual ~ClientChannel();
- virtual Protocol::RequestId popupRequest(const std::string &privilege);
+ virtual ConnectionContext popupRequest(const std::string &privilege);
private:
virtual void onAccept(int fd);
virtual void onClose(int fd);
virtual int onReceive(int fd, std::vector<std::string> &&message);
virtual void onSend(int fd);
- void init();
+ int connect();
AskUser::Protocol::RequestId generateRequestId();
ClientCallbacksPtr m_callbacks;
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017 - 2018 Samsung Electronics Co.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
typedef std::string Privilege;
typedef std::vector<Privilege> PrivilegeVector;
+struct ConnectionContext {
+ RequestId m_requestId;
+ ConnectionFd m_fd;
+};
+
const ConnectionFd INVALID_FD = -1;
const RequestId INVALID_ID = -1;