diff options
Diffstat (limited to 'src/components/policy/policy_external/src/policy_manager_impl.cc')
-rw-r--r-- | src/components/policy/policy_external/src/policy_manager_impl.cc | 1883 |
1 files changed, 1883 insertions, 0 deletions
diff --git a/src/components/policy/policy_external/src/policy_manager_impl.cc b/src/components/policy/policy_external/src/policy_manager_impl.cc new file mode 100644 index 0000000000..0a774f6b79 --- /dev/null +++ b/src/components/policy/policy_external/src/policy_manager_impl.cc @@ -0,0 +1,1883 @@ +/* + Copyright (c) 2013, Ford Motor Company + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the + distribution. + + Neither the name of the Ford Motor Company nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ +#include "policy/policy_manager_impl.h" + +#include <algorithm> +#include <set> +#include <queue> +#include <iterator> +#include <limits> +#include <vector> +#include <functional> +#include "json/reader.h" +#include "json/writer.h" +#include "policy/policy_table.h" +#include "policy/pt_representation.h" +#include "policy/policy_helper.h" +#include "utils/file_system.h" +#include "utils/logger.h" +#include "utils/date_time.h" +#include "policy/cache_manager.h" +#include "policy/update_status_manager.h" +#include "config_profile/profile.h" +#include "utils/make_shared.h" + +policy::PolicyManager* CreateManager() { + return new policy::PolicyManagerImpl(); +} +void DeleteManager(policy::PolicyManager* pm) { + delete pm; +} +namespace { + +/** + * @brief Extracts group name from group permission structure + */ +struct GroupNamesAppender + : public std::unary_function<void, + const policy::FunctionalGroupPermission&> { + GroupNamesAppender(policy_table::Strings& names) : names_(names) {} + + void operator()(const policy::FunctionalGroupPermission& value) { + names_.push_back(value.group_name); + } + + private: + policy_table::Strings& names_; +}; + +/** + * @brief Updates permission state of input group permission value in case + * group name is found within allowed or disallowed groups lists considering + * current priorities of consents + * Also collects matched groups names to separate collection for futher + * processing + */ +struct ConsentsUpdater + : public std::unary_function<void, policy::FunctionalGroupPermission&> { + ConsentsUpdater(const policy::GroupsNames& allowed, + const policy::GroupsNames& disallowed, + std::vector<policy::FunctionalGroupPermission>& + out_external_consent_matches, + const policy::ConsentPriorityType prio) + : allowed_(allowed) + , disallowed_(disallowed) + , out_external_consent_matches_(out_external_consent_matches) + , prio_(prio) {} + + void operator()(policy::FunctionalGroupPermission& value) { + if (helpers::in_range(disallowed_, value.group_name)) { + policy::FunctionalGroupPermission external_consent = value; + external_consent.state = policy::kGroupDisallowed; + out_external_consent_matches_.push_back(external_consent); + + if (IsAllowedToChangedUserConsent(value.state)) { + value.state = policy::kGroupDisallowed; + } + return; + } + + if (helpers::in_range(allowed_, value.group_name)) { + policy::FunctionalGroupPermission external_consent = value; + external_consent.state = policy::kGroupAllowed; + out_external_consent_matches_.push_back(external_consent); + + if (IsAllowedToChangedUserConsent(value.state)) { + value.state = policy::kGroupAllowed; + } + } + } + + private: + bool IsAllowedToChangedUserConsent( + policy::GroupConsent current_consent) const { + if (policy::GroupConsent::kGroupUndefined == current_consent) { + return true; + } + + return policy::ConsentPriorityType::kUserConsentPrio != prio_; + } + + const policy::GroupsNames& allowed_; + const policy::GroupsNames& disallowed_; + std::vector<policy::FunctionalGroupPermission>& out_external_consent_matches_; + const policy::ConsentPriorityType prio_; +}; + +/** + * @brief Checks whether ExternalConsent entity status is the same as name of + * group + * container where entity has been found in. In case of match group is added to + * 'disallowed' list, otherwise - to 'allowed' one. + * E.g. if entity has "ON" status and is found in + * 'disallowed_by_external_consent_entities_on' it will be added to + * 'disallowed'. If it has + * been found in 'disallowed_by_external_consent_entities_off' than group is + * added to + * 'allowed' list. + */ +struct GroupChecker + : std::unary_function< + void, + policy::GroupsByExternalConsentStatus::mapped_type::value_type> { + GroupChecker(const policy::EntityStatus entity_status, + policy::GroupsNames& out_allowed, + policy::GroupsNames& out_disallowed) + : entity_status_(entity_status) + , out_allowed_(out_allowed) + , out_disallowed_(out_disallowed) {} + + void operator()( + const policy::GroupsByExternalConsentStatus::mapped_type::value_type + value) { + using namespace policy; + + const std::string group_name = value.first; + + if ((value.second && (kStatusOn == entity_status_)) || + (!value.second && (kStatusOff == entity_status_))) { + out_disallowed_.insert(group_name); + } else { + out_allowed_.insert(group_name); + } + } + + private: + const policy::EntityStatus entity_status_; + policy::GroupsNames& out_allowed_; + policy::GroupsNames& out_disallowed_; +}; + +/** + * @brief Sorts groups for 'allowed' and 'disallowed' by ExternalConsent + * entities statuses. + * Wraps GroupChecker logic. + */ +struct GroupSorter + : std::unary_function< + void, + const policy::GroupsByExternalConsentStatus::value_type&> { + GroupSorter(policy::GroupsNames& out_allowed, + policy::GroupsNames& out_disallowed) + : out_allowed_(out_allowed), out_disallowed_(out_disallowed) {} + + void operator()( + const policy::GroupsByExternalConsentStatus::value_type& value) { + GroupChecker checker(value.first.status_, out_allowed_, out_disallowed_); + std::for_each(value.second.begin(), value.second.end(), checker); + } + + private: + policy::GroupsNames& out_allowed_; + policy::GroupsNames& out_disallowed_; +}; + +} // namespace + +namespace policy { + +CREATE_LOGGERPTR_GLOBAL(logger_, "Policy") + +PolicyManagerImpl::PolicyManagerImpl() + : PolicyManager() + , listener_(NULL) + , cache_(new CacheManager) + , retry_sequence_timeout_(60) + , retry_sequence_index_(0) + , ignition_check(true) + , retry_sequence_url_(0, 0, "") {} + +PolicyManagerImpl::PolicyManagerImpl(bool in_memory) + : PolicyManager() + , listener_(NULL) + , cache_(new CacheManager(in_memory)) + , retry_sequence_timeout_(60) + , retry_sequence_index_(0) + , ignition_check(true) + , retry_sequence_url_(0, 0, "") + , wrong_ptu_update_received_(false) + , send_on_update_sent_out_(false) + , trigger_ptu_(false) {} + +void PolicyManagerImpl::set_listener(PolicyListener* listener) { + listener_ = listener; + update_status_manager_.set_listener(listener); +} + +utils::SharedPtr<policy_table::Table> PolicyManagerImpl::Parse( + const BinaryMessage& pt_content) { + std::string json(pt_content.begin(), pt_content.end()); + Json::Value value; + Json::Reader reader; + if (reader.parse(json.c_str(), value)) { + return new policy_table::Table(&value); + } else { + return utils::SharedPtr<policy_table::Table>(); + } +} + +void PolicyManagerImpl::CheckTriggers() { + LOG4CXX_AUTO_TRACE(logger_); + const bool exceed_ignition_cycles = ExceededIgnitionCycles(); + const bool exceed_days = ExceededDays(); + + LOG4CXX_DEBUG( + logger_, + "\nDays exceeded: " << std::boolalpha << exceed_days + << "\nIgnition cycles exceeded: " << std::boolalpha + << exceed_ignition_cycles); + + if (exceed_ignition_cycles || exceed_days) { + update_status_manager_.ScheduleUpdate(); + } +} + +std::string PolicyManagerImpl::GetLockScreenIconUrl() const { + return cache_->GetLockScreenIconUrl(); +} + +bool PolicyManagerImpl::LoadPT(const std::string& file, + const BinaryMessage& pt_content) { + LOG4CXX_INFO(logger_, "LoadPT of size " << pt_content.size()); + + // Parse message into table struct + utils::SharedPtr<policy_table::Table> pt_update = Parse(pt_content); + if (!pt_update) { + LOG4CXX_WARN(logger_, "Parsed table pointer is NULL."); + update_status_manager_.OnWrongUpdateReceived(); + return false; + } + + file_system::DeleteFile(file); + + if (!IsPTValid(pt_update, policy_table::PT_UPDATE)) { + wrong_ptu_update_received_ = true; + update_status_manager_.OnWrongUpdateReceived(); + return false; + } + + update_status_manager_.OnValidUpdateReceived(); + cache_->SaveUpdateRequired(false); + + { + sync_primitives::AutoLock lock(apps_registration_lock_); + + // Get current DB data, since it could be updated during awaiting of PTU + utils::SharedPtr<policy_table::Table> policy_table_snapshot = + cache_->GenerateSnapshot(); + if (!policy_table_snapshot) { + LOG4CXX_ERROR(logger_, "Failed to create snapshot of policy table"); + return false; + } + + // Checking of difference between PTU and current policy state + // Must to be done before PTU applying since it is possible, that functional + // groups, which had been present before are absent in PTU and will be + // removed after update. So in case of revoked groups system has to know + // names and ids of revoked groups before they will be removed. + CheckAppPolicyResults results = + CheckPermissionsChanges(pt_update, policy_table_snapshot); + + // Replace current data with updated + if (!cache_->ApplyUpdate(*pt_update)) { + LOG4CXX_WARN(logger_, "Unsuccessful save of updated policy table."); + return false; + } + + ExternalConsentStatus status = cache_->GetExternalConsentStatus(); + GroupsByExternalConsentStatus groups_by_status = + cache_->GetGroupsWithSameEntities(status); + + ProcessExternalConsentStatusUpdate( + groups_by_status, ConsentProcessingPolicy::kExternalConsentBased); + + ProcessAppPolicyCheckResults( + results, pt_update->policy_table.app_policies_section.apps); + + listener_->OnCertificateUpdated( + *(pt_update->policy_table.module_config.certificate)); + + std::map<std::string, StringArray> app_hmi_types; + cache_->GetHMIAppTypeAfterUpdate(app_hmi_types); + if (!app_hmi_types.empty()) { + LOG4CXX_INFO(logger_, "app_hmi_types is full calling OnUpdateHMIAppType"); + listener_->OnUpdateHMIAppType(app_hmi_types); + } else { + LOG4CXX_INFO(logger_, "app_hmi_types empty"); + } + } + + // If there was a user request for policy table update, it should be started + // right after current update is finished + if (update_status_manager_.IsUpdateRequired()) { + StartPTExchange(); + return true; + } + + RefreshRetrySequence(); + return true; +} + +CheckAppPolicyResults PolicyManagerImpl::CheckPermissionsChanges( + const utils::SharedPtr<policy_table::Table> pt_update, + const utils::SharedPtr<policy_table::Table> snapshot) { + LOG4CXX_INFO(logger_, "Checking incoming permissions."); + + // Replace predefined policies with its actual setting, e.g. "123":"default" + // to actual values of default section + UnwrapAppPolicies(pt_update->policy_table.app_policies_section.apps); + + CheckAppPolicyResults out_results; + std::for_each(pt_update->policy_table.app_policies_section.apps.begin(), + pt_update->policy_table.app_policies_section.apps.end(), + CheckAppPolicy(this, pt_update, snapshot, out_results)); + + return out_results; +} + +void PolicyManagerImpl::ProcessAppPolicyCheckResults( + const CheckAppPolicyResults& results, + const policy_table::ApplicationPolicies& app_policies) { + CheckAppPolicyResults::const_iterator it_results = results.begin(); + + for (; results.end() != it_results; ++it_results) { + const policy_table::ApplicationPolicies::const_iterator app_policy = + app_policies.find(it_results->first); + + if (app_policies.end() == app_policy) { + continue; + } + + if (IsPredefinedApp(*app_policy)) { + continue; + } + + switch (it_results->second) { + case RESULT_NO_CHANGES: + continue; + case RESULT_APP_REVOKED: + NotifySystem(*app_policy); + continue; + case RESULT_NICKNAME_MISMATCH: + NotifySystem(*app_policy); + continue; + case RESULT_CONSENT_NEEDED: + case RESULT_PERMISSIONS_REVOKED_AND_CONSENT_NEEDED: { + // Post-check after ExternalConsent consent changes + const std::string policy_app_id = app_policy->first; + if (!IsConsentNeeded(policy_app_id)) { + sync_primitives::AutoLock lock(app_permissions_diff_lock_); + + PendingPermissions::iterator app_id_diff = + app_permissions_diff_.find(policy_app_id); + + if (app_permissions_diff_.end() != app_id_diff) { + app_id_diff->second.appPermissionsConsentNeeded = false; + } + } + } break; + case RESULT_CONSENT_NOT_REQIURED: + case RESULT_PERMISSIONS_REVOKED: + case RESULT_REQUEST_TYPE_CHANGED: + break; + default: + continue; + } + NotifySystem(*app_policy); + SendPermissionsToApp(*app_policy); + } +} + +void PolicyManagerImpl::PrepareNotificationData( + const policy_table::FunctionalGroupings& groups, + const policy_table::Strings& group_names, + const std::vector<FunctionalGroupPermission>& group_permission, + Permissions& notification_data) { + LOG4CXX_INFO(logger_, "Preparing data for notification."); + ProcessFunctionalGroup processor(groups, group_permission, notification_data); + std::for_each(group_names.begin(), group_names.end(), processor); +} + +std::string PolicyManagerImpl::GetUpdateUrl(int service_type) { + LOG4CXX_AUTO_TRACE(logger_); + EndpointUrls urls; + GetUpdateUrls(service_type, urls); + + std::string url; + if (!urls.empty()) { + static uint32_t index = 0; + + if (index >= urls.size()) { + index = 0; + } + url = urls[index].url.empty() ? "" : urls[index].url[0]; + + ++index; + } else { + LOG4CXX_ERROR(logger_, "The endpoint entry is empty"); + } + return url; +} + +void PolicyManagerImpl::GetUpdateUrls(const std::string& service_type, + EndpointUrls& out_end_points) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->GetUpdateUrls(service_type, out_end_points); +} +void PolicyManagerImpl::GetUpdateUrls(const uint32_t service_type, + EndpointUrls& out_end_points) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->GetUpdateUrls(service_type, out_end_points); +} + +void PolicyManagerImpl::RequestPTUpdate() { + LOG4CXX_AUTO_TRACE(logger_); + utils::SharedPtr<policy_table::Table> policy_table_snapshot = + cache_->GenerateSnapshot(); + if (!policy_table_snapshot) { + LOG4CXX_ERROR(logger_, "Failed to create snapshot of policy table"); + return; + } + + if (IsPTValid(policy_table_snapshot, policy_table::PT_SNAPSHOT)) { + Json::Value value = policy_table_snapshot->ToJsonValue(); + Json::FastWriter writer; + std::string message_string = writer.write(value); + + LOG4CXX_DEBUG(logger_, "Snapshot contents is : " << message_string); + + BinaryMessage update(message_string.begin(), message_string.end()); + + listener_->OnSnapshotCreated( + update, RetrySequenceDelaysSeconds(), TimeoutExchangeMSec()); + } else { + LOG4CXX_ERROR(logger_, "Invalid Policy table snapshot - PTUpdate failed"); + } +} + +void PolicyManagerImpl::StartPTExchange() { + LOG4CXX_AUTO_TRACE(logger_); + + if (ignition_check) { + CheckTriggers(); + ignition_check = false; + } + + if (update_status_manager_.IsAppsSearchInProgress() && + update_status_manager_.IsUpdateRequired()) { + LOG4CXX_INFO(logger_, + "Starting exchange skipped, since applications " + "search is in progress."); + return; + } + + if (update_status_manager_.IsUpdatePending()) { + if (trigger_ptu_) { + update_status_manager_.ScheduleUpdate(); + } + LOG4CXX_INFO(logger_, + "Starting exchange skipped, since another exchange " + "is in progress."); + return; + } + LOG4CXX_INFO(logger_, "Policy want to call RequestPTUpdate"); + if (listener_ && listener_->CanUpdate()) { + LOG4CXX_INFO(logger_, "Listener CanUpdate"); + if (update_status_manager_.IsUpdateRequired()) { + LOG4CXX_INFO(logger_, "IsUpdateRequired"); + RequestPTUpdate(); + } + } +} + +void PolicyManagerImpl::OnAppsSearchStarted() { + LOG4CXX_AUTO_TRACE(logger_); + update_status_manager_.OnAppsSearchStarted(); +} + +void PolicyManagerImpl::OnAppsSearchCompleted(const bool trigger_ptu) { + LOG4CXX_AUTO_TRACE(logger_); + update_status_manager_.OnAppsSearchCompleted(); + + trigger_ptu_ = trigger_ptu; + + if (update_status_manager_.IsUpdateRequired()) { + StartPTExchange(); + } +} + +const std::vector<std::string> PolicyManagerImpl::GetAppRequestTypes( + const std::string policy_app_id) const { + std::vector<std::string> request_types; + if (kDeviceDisallowed == + cache_->GetDeviceConsent(GetCurrentDeviceId(policy_app_id))) { + cache_->GetAppRequestTypes(kPreDataConsentId, request_types); + } else { + cache_->GetAppRequestTypes(policy_app_id, request_types); + } + return request_types; +} + +const VehicleInfo PolicyManagerImpl::GetVehicleInfo() const { + return cache_->GetVehicleInfo(); +} + +void PolicyManagerImpl::CheckPermissions(const PTString& app_id, + const PTString& hmi_level, + const PTString& rpc, + const RPCParams& rpc_params, + CheckPermissionResult& result) { + LOG4CXX_INFO(logger_, + "CheckPermissions for " << app_id << " and rpc " << rpc + << " for " << hmi_level << " level."); + + const std::string device_id = GetCurrentDeviceId(app_id); + + Permissions rpc_permissions; + + // Check, if there are calculated permission present in cache + if (!cache_->IsPermissionsCalculated(device_id, app_id, rpc_permissions)) { + LOG4CXX_DEBUG(logger_, + "IsPermissionsCalculated for device: " + << device_id << " and app: " << app_id + << " returns false"); + // Get actual application group permission according to user consents + std::vector<FunctionalGroupPermission> app_group_permissions; + GetPermissionsForApp(device_id, app_id, app_group_permissions); + + // Fill struct with known groups RPCs + policy_table::FunctionalGroupings functional_groupings; + cache_->GetFunctionalGroupings(functional_groupings); + + policy_table::Strings app_groups = GetGroupsNames(app_group_permissions); + + // Undefined groups (without user consent) disallowed by default, since + // OnPermissionsChange notification has no "undefined" section + // For RPC permission checking undefinded group will be treated as separate + // type + ProcessFunctionalGroup processor(functional_groupings, + app_group_permissions, + rpc_permissions, + GroupConsent::kGroupUndefined); + std::for_each(app_groups.begin(), app_groups.end(), processor); + + cache_->AddCalculatedPermissions(device_id, app_id, rpc_permissions); + } else { + LOG4CXX_DEBUG(logger_, + "IsPermissionsCalculated for device: " + << device_id << " and app: " << app_id + << " returns true"); + } + + const bool known_rpc = rpc_permissions.end() != rpc_permissions.find(rpc); + LOG4CXX_INFO(logger_, "Is known rpc " << known_rpc); + if (!known_rpc) { + // RPC not found in list == disallowed by backend + result.hmi_level_permitted = kRpcDisallowed; + return; + } + + // Check HMI level + if (rpc_permissions[rpc].hmi_permissions[kAllowedKey].end() != + rpc_permissions[rpc].hmi_permissions[kAllowedKey].find(hmi_level)) { + // RPC found in allowed == allowed by backend and user + result.hmi_level_permitted = kRpcAllowed; + } else if (rpc_permissions[rpc].hmi_permissions[kUndefinedKey].end() != + rpc_permissions[rpc].hmi_permissions[kUndefinedKey].find( + hmi_level)) { + // RPC found in undefined == allowed by backend, but not consented yet by + // user + result.hmi_level_permitted = kRpcDisallowed; + } else if (rpc_permissions[rpc].hmi_permissions[kUserDisallowedKey].end() != + rpc_permissions[rpc].hmi_permissions[kUserDisallowedKey].find( + hmi_level)) { + // RPC found in allowed == allowed by backend, but disallowed by user + result.hmi_level_permitted = kRpcUserDisallowed; + } else { + LOG4CXX_DEBUG(logger_, + "HMI level " << hmi_level << " wasn't found " + << " for rpc " << rpc << " and appID " + << app_id); + return; + } + + if (kRpcAllowed != result.hmi_level_permitted) { + LOG4CXX_DEBUG(logger_, "RPC is not allowed. Stop parameters processing."); + result.list_of_allowed_params = + rpc_permissions[rpc].parameter_permissions[kAllowedKey]; + + result.list_of_disallowed_params = + rpc_permissions[rpc].parameter_permissions[kUserDisallowedKey]; + + result.list_of_undefined_params = + rpc_permissions[rpc].parameter_permissions[kUndefinedKey]; + return; + } + + // Considered that items disallowed by user take priority over system (policy) + // permissions, so that flag is processed first + if (rpc_permissions[rpc] + .parameter_permissions.any_parameter_disallowed_by_user) { + LOG4CXX_DEBUG(logger_, "All parameters are disallowed by user."); + result.list_of_disallowed_params = rpc_params; + result.hmi_level_permitted = kRpcUserDisallowed; + return; + } + + if (rpc_permissions[rpc] + .parameter_permissions.any_parameter_disallowed_by_policy) { + LOG4CXX_DEBUG(logger_, "All parameters are disallowed by policy."); + result.list_of_undefined_params = rpc_params; + result.hmi_level_permitted = kRpcDisallowed; + return; + } + + if (rpc_permissions[rpc].parameter_permissions.any_parameter_allowed) { + LOG4CXX_DEBUG(logger_, "All parameters are allowed."); + result.list_of_allowed_params = rpc_params; + return; + } + + result.list_of_allowed_params = + rpc_permissions[rpc].parameter_permissions[kAllowedKey]; + + result.list_of_disallowed_params = + rpc_permissions[rpc].parameter_permissions[kUserDisallowedKey]; + + result.list_of_undefined_params = + rpc_permissions[rpc].parameter_permissions[kUndefinedKey]; + + // In case of some parameters of RPC are missing in current policy table + // they will be considered as disallowed by policy itself, not by user. + // Undefined parameters contain parameters present in policy table, but which + // have not been allowed or disallowed explicitly by user, so missing params + // are being added to undefined. + RPCParams::const_iterator parameter = rpc_params.begin(); + RPCParams::const_iterator end = rpc_params.end(); + for (; end != parameter; ++parameter) { + if (!result.HasParameter(*parameter)) { + LOG4CXX_DEBUG(logger_, + "Parameter " << *parameter << " is unknown." + " Adding to undefined list."); + result.list_of_undefined_params.insert(*parameter); + } + } + + if (result.DisallowedInclude(rpc_params)) { + LOG4CXX_DEBUG(logger_, "All parameters are disallowed."); + result.hmi_level_permitted = kRpcUserDisallowed; + } else if (!result.IsAnyAllowed(rpc_params)) { + LOG4CXX_DEBUG(logger_, "There are no parameters allowed."); + result.hmi_level_permitted = kRpcDisallowed; + } + + if (cache_->IsApplicationRevoked(app_id)) { + // SDL must be able to notify mobile side with its status after app has + // been revoked by backend + if ("OnHMIStatus" == rpc && "NONE" == hmi_level) { + result.hmi_level_permitted = kRpcAllowed; + } else { + result.hmi_level_permitted = kRpcDisallowed; + } + return; + } +} + +bool PolicyManagerImpl::ResetUserConsent() { + bool result = true; + result = cache_->ResetUserConsent(); + + return result; +} + +policy_table::Strings PolicyManagerImpl::GetGroupsNames( + const std::vector<FunctionalGroupPermission>& app_group_permissions) const { + policy_table::Strings app_groups; + GroupNamesAppender appender(app_groups); + std::for_each( + app_group_permissions.begin(), app_group_permissions.end(), appender); + + return app_groups; +} + +void PolicyManagerImpl::SendNotificationOnPermissionsUpdated( + const std::string& application_id) { + LOG4CXX_AUTO_TRACE(logger_); + const std::string device_id = GetCurrentDeviceId(application_id); + if (device_id.empty()) { + LOG4CXX_WARN(logger_, + "Couldn't find device info for application id " + "'" << application_id << "'"); + return; + } + + std::vector<FunctionalGroupPermission> app_group_permissions; + GetPermissionsForApp(device_id, application_id, app_group_permissions); + + policy_table::FunctionalGroupings functional_groupings; + cache_->GetFunctionalGroupings(functional_groupings); + + policy_table::Strings app_groups = GetGroupsNames(app_group_permissions); + + Permissions notification_data; + PrepareNotificationData(functional_groupings, + app_groups, + app_group_permissions, + notification_data); + + LOG4CXX_INFO(logger_, + "Send notification for application_id:" << application_id); + + std::string default_hmi; + GetDefaultHmi(application_id, &default_hmi); + + listener()->OnPermissionsUpdated( + application_id, notification_data, default_hmi); +} + +bool PolicyManagerImpl::CleanupUnpairedDevices() { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->CleanupUnpairedDevices(); +} + +DeviceConsent PolicyManagerImpl::GetUserConsentForDevice( + const std::string& device_id) const { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->GetDeviceConsent(device_id); +} + +void PolicyManagerImpl::SetUserConsentForDevice(const std::string& device_id, + const bool is_allowed) { + LOG4CXX_AUTO_TRACE(logger_); + LOG4CXX_DEBUG(logger_, "Device :" << device_id); + cache_->SetDeviceConsent(device_id, is_allowed); + if (listener_) { + listener_->OnDeviceConsentChanged(device_id, is_allowed); + } else { + LOG4CXX_WARN(logger_, + "Event listener is not initialized. " + "Can't call OnDeviceConsentChanged"); + } + if (is_allowed) { + update_status_manager_.OnDeviceConsented(); + } + StartPTExchange(); +} + +bool PolicyManagerImpl::ReactOnUserDevConsentForApp( + const std::string& app_id, const bool is_device_allowed) { + std::vector<std::string> current_request_types = GetAppRequestTypes(app_id); + std::string current_priority, new_priority; + GetPriority(app_id, ¤t_priority); + + bool result = cache_->ReactOnUserDevConsentForApp(app_id, is_device_allowed); + + std::vector<std::string> new_request_types = GetAppRequestTypes(app_id); + GetPriority(app_id, &new_priority); + std::sort(current_request_types.begin(), current_request_types.end()); + std::sort(new_request_types.begin(), new_request_types.end()); + + std::vector<std::string> diff; + std::set_symmetric_difference(current_request_types.begin(), + current_request_types.end(), + new_request_types.begin(), + new_request_types.end(), + std::back_inserter(diff)); + + AppPermissions permissions(app_id); + + if (!diff.empty()) { + permissions.requestType = new_request_types; + permissions.requestTypeChanged = true; + } + + if ((!current_priority.empty()) && (!new_priority.empty()) && + (current_priority != new_priority)) { + permissions.priority = new_priority; + } + + if (permissions.requestTypeChanged || (!permissions.priority.empty())) { + listener_->SendOnAppPermissionsChanged(permissions, app_id); + } + return result; +} + +bool PolicyManagerImpl::GetInitialAppData(const std::string& application_id, + StringArray* nicknames, + StringArray* app_hmi_types) { + LOG4CXX_AUTO_TRACE(logger_); + const bool result = nicknames && app_hmi_types; + if (result) { + cache_->GetInitialAppData(application_id, *nicknames, *app_hmi_types); + } + return result; +} + +void PolicyManagerImpl::AddDevice(const std::string& device_id, + const std::string& connection_type) { + LOG4CXX_AUTO_TRACE(logger_); + LOG4CXX_DEBUG(logger_, "Device: " << device_id); + if (!cache_->AddDevice(device_id, connection_type)) { + LOG4CXX_WARN(logger_, "Can't add device."); + } +} + +void PolicyManagerImpl::SetDeviceInfo(const std::string& device_id, + const DeviceInfo& device_info) { + LOG4CXX_AUTO_TRACE(logger_); + LOG4CXX_DEBUG(logger_, "Device :" << device_id); + if (!cache_->SetDeviceData(device_id, + device_info.hardware, + device_info.firmware_rev, + device_info.os, + device_info.os_ver, + device_info.carrier, + device_info.max_number_rfcom_ports, + device_info.connection_type)) { + LOG4CXX_WARN(logger_, "Can't set device data."); + } +} + +PermissionConsent PolicyManagerImpl::EnsureCorrectPermissionConsent( + const PermissionConsent& permissions_to_check) { + std::vector<FunctionalGroupPermission> current_user_consents; + GetUserConsentForApp(permissions_to_check.device_id, + permissions_to_check.policy_app_id, + current_user_consents); + + PermissionConsent permissions_to_set; + permissions_to_set.device_id = permissions_to_check.device_id; + permissions_to_set.policy_app_id = permissions_to_check.policy_app_id; + permissions_to_set.consent_source = permissions_to_check.consent_source; + + std::vector<FunctionalGroupPermission>::const_iterator it = + permissions_to_check.group_permissions.begin(); + std::vector<FunctionalGroupPermission>::const_iterator it_end = + permissions_to_check.group_permissions.end(); + + for (; it != it_end; ++it) { + std::vector<FunctionalGroupPermission>::const_iterator it_curr = + current_user_consents.begin(); + std::vector<FunctionalGroupPermission>::const_iterator it_curr_end = + current_user_consents.end(); + + for (; it_curr != it_curr_end; ++it_curr) { + if (it->group_alias == it_curr->group_alias && + it->group_id == it_curr->group_id) { + permissions_to_set.group_permissions.push_back(*it); + } + } + } + + return permissions_to_set; +} + +void PolicyManagerImpl::CheckPendingPermissionsChanges( + const std::string& policy_app_id, + const std::vector<FunctionalGroupPermission>& current_permissions) { + LOG4CXX_AUTO_TRACE(logger_); + sync_primitives::AutoLock lock(app_permissions_diff_lock_); + PendingPermissions::iterator it_pending = + app_permissions_diff_.find(policy_app_id); + if (app_permissions_diff_.end() == it_pending) { + LOG4CXX_WARN( + logger_, + "No pending permissions had been found for appID: " << policy_app_id); + return; + } + + LOG4CXX_DEBUG( + logger_, + "Pending permissions had been found for appID: " << policy_app_id); + + // Change appPermissionsConsentNeeded depending on unconsented groups + // presence + std::vector<policy::FunctionalGroupPermission>::const_iterator it_groups = + current_permissions.begin(); + std::vector<policy::FunctionalGroupPermission>::const_iterator it_end_groups = + current_permissions.end(); + + for (; it_groups != it_end_groups; ++it_groups) { + if (policy::kGroupUndefined == it_groups->state) { + LOG4CXX_DEBUG( + logger_, + "Unconsented groups still present for appID: " << policy_app_id); + it_pending->second.appPermissionsConsentNeeded = true; + return; + } + } + + LOG4CXX_DEBUG( + logger_, + "Unconsented groups not present anymore for appID: " << policy_app_id); + it_pending->second.appPermissionsConsentNeeded = false; + return; +} + +void PolicyManagerImpl::NotifyPermissionsChanges( + const std::string& policy_app_id, + const std::vector<FunctionalGroupPermission>& app_group_permissions) { + // Get current functional groups from DB with RPC permissions + policy_table::FunctionalGroupings functional_groups; + cache_->GetFunctionalGroupings(functional_groups); + + // Get list of groups assigned to application + policy_table::Strings app_groups = GetGroupsNames(app_group_permissions); + + // Fill notification data according to group permissions + Permissions notification_data; + PrepareNotificationData( + functional_groups, app_groups, app_group_permissions, notification_data); + + listener()->OnPermissionsUpdated(policy_app_id, notification_data); +} + +void PolicyManagerImpl::SetUserConsentForApp( + const PermissionConsent& permissions, const NotificationMode mode) { + LOG4CXX_AUTO_TRACE(logger_); + + if (permissions.group_permissions.empty()) { + LOG4CXX_DEBUG(logger_, "Permissions list is empty, skipping update."); + return; + } + + cache_->ResetCalculatedPermissions(); + PermissionConsent verified_permissions = + EnsureCorrectPermissionConsent(permissions); + + bool app_permissions_changed = false; + if (!cache_->SetUserPermissionsForApp(verified_permissions, + &app_permissions_changed)) { + LOG4CXX_WARN(logger_, "Can't set user permissions for application."); + return; + } + + if (kSilentMode == mode) { + LOG4CXX_WARN(logger_, + "Silent mode is enabled. Application won't be informed."); + return; + } + + if (!app_permissions_changed) { + LOG4CXX_WARN(logger_, + "Application already has same consents. " + "Notificaton won't be sent."); + return; + } + + std::vector<FunctionalGroupPermission> updated_app_group_permissons; + GetPermissionsForApp(verified_permissions.device_id, + verified_permissions.policy_app_id, + updated_app_group_permissons); + + CheckPendingPermissionsChanges(verified_permissions.policy_app_id, + updated_app_group_permissons); + + NotifyPermissionsChanges(verified_permissions.policy_app_id, + updated_app_group_permissons); +} + +bool PolicyManagerImpl::GetDefaultHmi(const std::string& policy_app_id, + std::string* default_hmi) const { + LOG4CXX_AUTO_TRACE(logger_); + const std::string device_id = GetCurrentDeviceId(policy_app_id); + DeviceConsent device_consent = GetUserConsentForDevice(device_id); + const std::string app_id = policy::kDeviceAllowed != device_consent + ? kPreDataConsentId + : policy_app_id; + return cache_->GetDefaultHMI(app_id, *default_hmi); +} + +bool PolicyManagerImpl::GetPriority(const std::string& policy_app_id, + std::string* priority) const { + LOG4CXX_AUTO_TRACE(logger_); + if (!priority) { + LOG4CXX_WARN(logger_, "Input priority parameter is null."); + return false; + } + + return cache_->GetPriority(policy_app_id, *priority); +} + +std::vector<UserFriendlyMessage> PolicyManagerImpl::GetUserFriendlyMessages( + const std::vector<std::string>& message_code, + const std::string& language, + const std::string& active_hmi_language) { + return cache_->GetUserFriendlyMsg( + message_code, language, active_hmi_language); +} + +void PolicyManagerImpl::GetUserConsentForApp( + const std::string& device_id, + const std::string& policy_app_id, + std::vector<FunctionalGroupPermission>& permissions) { + LOG4CXX_AUTO_TRACE(logger_); + + FunctionalIdType group_types; + if (!cache_->GetPermissionsForApp(device_id, policy_app_id, group_types)) { + LOG4CXX_WARN(logger_, + "Can't get user permissions for app " << policy_app_id); + return; + } + + // Functional groups w/o alias ("user_consent_prompt") considered as + // automatically allowed and it could not be changed by user + FunctionalGroupNames group_names; + if (!cache_->GetFunctionalGroupNames(group_names)) { + LOG4CXX_WARN(logger_, "Can't get functional group names"); + return; + } + + FunctionalGroupNames::const_iterator it = group_names.begin(); + FunctionalGroupNames::const_iterator it_end = group_names.end(); + FunctionalGroupIDs auto_allowed_groups; + for (; it != it_end; ++it) { + if (it->second.first.empty()) { + auto_allowed_groups.push_back(it->first); + } + } + + FunctionalGroupIDs all_groups = group_types[kTypeGeneral]; + FunctionalGroupIDs preconsented_groups = group_types[kTypePreconsented]; + FunctionalGroupIDs consent_allowed_groups = group_types[kTypeAllowed]; + FunctionalGroupIDs consent_disallowed_groups = group_types[kTypeDisallowed]; + FunctionalGroupIDs default_groups = group_types[kTypeDefault]; + FunctionalGroupIDs predataconsented_groups = + group_types[kTypePreDataConsented]; + FunctionalGroupIDs device_groups = group_types[kTypeDevice]; + + // Sorting groups by consent + FunctionalGroupIDs preconsented_wo_auto = + ExcludeSame(preconsented_groups, auto_allowed_groups); + + FunctionalGroupIDs preconsented_wo_disallowed_auto = + ExcludeSame(preconsented_wo_auto, consent_disallowed_groups); + + FunctionalGroupIDs allowed_groups = + Merge(consent_allowed_groups, preconsented_wo_disallowed_auto); + + FunctionalGroupIDs merged_stage_1 = + Merge(default_groups, predataconsented_groups); + + FunctionalGroupIDs merged_stage_2 = Merge(merged_stage_1, device_groups); + + FunctionalGroupIDs merged_stage_3 = + Merge(merged_stage_2, auto_allowed_groups); + + FunctionalGroupIDs excluded_stage_1 = ExcludeSame(all_groups, merged_stage_3); + + FunctionalGroupIDs excluded_stage_2 = + ExcludeSame(excluded_stage_1, consent_disallowed_groups); + + FunctionalGroupIDs undefined_consent = + ExcludeSame(excluded_stage_2, allowed_groups); + + // Fill result + FillFunctionalGroupPermissions( + undefined_consent, group_names, kGroupUndefined, permissions); + FillFunctionalGroupPermissions( + allowed_groups, group_names, kGroupAllowed, permissions); + FillFunctionalGroupPermissions( + consent_disallowed_groups, group_names, kGroupDisallowed, permissions); +} + +void PolicyManagerImpl::GetPermissionsForApp( + const std::string& device_id, + const std::string& policy_app_id, + std::vector<FunctionalGroupPermission>& permissions) { + LOG4CXX_AUTO_TRACE(logger_); + std::string app_id_to_check = policy_app_id; + + if (!cache_->IsApplicationRepresented(policy_app_id)) { + LOG4CXX_WARN(logger_, + "Application with id " << policy_app_id + << " is not found within known apps."); + return; + } + + bool allowed_by_default = false; + if (cache_->IsDefaultPolicy(policy_app_id)) { + app_id_to_check = kDefaultId; + allowed_by_default = true; + } else if (cache_->IsPredataPolicy(policy_app_id) || + policy::kDeviceDisallowed == GetUserConsentForDevice(device_id)) { + app_id_to_check = kPreDataConsentId; + allowed_by_default = true; + } + + FunctionalIdType group_types; + if (!cache_->GetPermissionsForApp(device_id, app_id_to_check, group_types)) { + LOG4CXX_WARN(logger_, + "Can't get user permissions for app " << policy_app_id); + return; + } + + // Functional groups w/o alias ("user_consent_prompt") considered as + // automatically allowed and it could not be changed by user + FunctionalGroupNames group_names; + if (!cache_->GetFunctionalGroupNames(group_names)) { + LOG4CXX_WARN(logger_, "Can't get functional group names"); + return; + } + + // The "default" and "pre_DataConsent" are auto-allowed groups + // So, check if application in the one of these mode. + if (allowed_by_default) { + LOG4CXX_INFO(logger_, "Get auto allowed groups"); + GroupType type = + (kDefaultId == app_id_to_check ? kTypeDefault : kTypePreDataConsented); + + FillFunctionalGroupPermissions( + group_types[type], group_names, kGroupAllowed, permissions); + } else { + // The code bellow allows to process application which + // has specific permissions(not default and pre_DataConsent). + + // All groups for specific application + FunctionalGroupIDs all_groups = group_types[kTypeGeneral]; + + // Groups assigned by the user for specific application + FunctionalGroupIDs allowed_groups = group_types[kTypeAllowed]; + + // Groups disallowed by the user for specific application + FunctionalGroupIDs common_disallowed = group_types[kTypeDisallowed]; + + // Groups that allowed by default but can be changed by the user + FunctionalGroupIDs preconsented_groups = group_types[kTypePreconsented]; + + // Groups which has user consent promt but there is no any consnets now. + FunctionalGroupIDs unconsented_groups = group_types[kTypeUnconsented]; + + // Pull common groups from allowed and preconsented parts. + FunctionalGroupIDs allowed_preconsented = + Merge(allowed_groups, preconsented_groups); + + // Get all groups that we suppose are allowed. + FunctionalGroupIDs all_allowed = Merge(allowed_preconsented, all_groups); + + // In case when same groups exists in disallowed and allowed tables, + // disallowed one have priority over allowed. So we have to remove + // all disallowed groups from allowed table. + FunctionalGroupIDs common_allowed = + ExcludeSame(all_allowed, common_disallowed); + FunctionalGroupIDs consent_disallowed = + ExcludeSame(unconsented_groups, preconsented_groups); + + // Disallowed groups are contain all directly disallowed, + // plus unconsented minus preconsented. + FunctionalGroupIDs all_disallowed = + Merge(common_disallowed, consent_disallowed); + + // Fill result + FillFunctionalGroupPermissions( + consent_disallowed, group_names, kGroupUndefined, permissions); + FillFunctionalGroupPermissions( + common_allowed, group_names, kGroupAllowed, permissions); + FillFunctionalGroupPermissions( + all_disallowed, group_names, kGroupDisallowed, permissions); + } + return; +} + +std::string& PolicyManagerImpl::GetCurrentDeviceId( + const std::string& policy_app_id) const { + LOG4CXX_INFO(logger_, "GetDeviceInfo"); + last_device_id_ = listener()->OnCurrentDeviceIdUpdateRequired(policy_app_id); + return last_device_id_; +} + +void PolicyManagerImpl::SetSystemLanguage(const std::string& language) { + cache_->SetSystemLanguage(language); +} + +void PolicyManagerImpl::SetSystemInfo(const std::string& ccpu_version, + const std::string& wers_country_code, + const std::string& language) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->SetMetaInfo(ccpu_version, wers_country_code, language); +} + +void PolicyManagerImpl::OnSystemReady() { + // Update policy table for the first time with system information + if (!cache_->IsMetaInfoPresent()) { + listener()->OnSystemInfoUpdateRequired(); + } +} + +uint32_t PolicyManagerImpl::GetNotificationsNumber( + const std::string& priority) const { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->GetNotificationsNumber(priority); +} + +bool PolicyManagerImpl::ExceededIgnitionCycles() { + return 0 == cache_->IgnitionCyclesBeforeExchange(); +} + +bool PolicyManagerImpl::IsPTValid( + utils::SharedPtr<policy_table::Table> policy_table, + policy_table::PolicyTableType type) const { + policy_table->SetPolicyTableType(type); + if (!policy_table->is_valid()) { + LOG4CXX_ERROR(logger_, "Policy table is not valid."); + rpc::ValidationReport report("policy_table"); + policy_table->ReportErrors(&report); + LOG4CXX_DEBUG(logger_, "Errors: " << rpc::PrettyFormat(report)); + return false; + } + return true; +} + +const PolicySettings& PolicyManagerImpl::get_settings() const { + DCHECK(settings_); + return *settings_; +} + +void PolicyManagerImpl::UpdateAppConsentWithExternalConsent( + const std::string& device_id, + const std::string& application_id, + const GroupsNames& allowed_groups, + const GroupsNames& disallowed_groups, + const ConsentProcessingPolicy processing_policy) { + LOG4CXX_AUTO_TRACE(logger_); + + if (allowed_groups.empty() && disallowed_groups.empty()) { + LOG4CXX_DEBUG(logger_, + "Allowed and disallowed groups are empty, skipping update by " + "external user consent."); + return; + } + + std::vector<FunctionalGroupPermission> current_permissions; + GetUserConsentForApp(device_id, application_id, current_permissions); + + ConsentPriorityType prio = ConsentPriorityType::kExternalConsentPrio; + if (ConsentProcessingPolicy::kTimestampBased == processing_policy) { + prio = cache_->GetConsentsPriority(device_id, application_id); + } + + std::vector<FunctionalGroupPermission> external_consent_groups_matches; + ConsentsUpdater updater( + allowed_groups, disallowed_groups, external_consent_groups_matches, prio); + std::for_each( + current_permissions.begin(), current_permissions.end(), updater); + + const std::string source = "GUI"; + + PermissionConsent updated_user_permissions; + updated_user_permissions.group_permissions = current_permissions; + updated_user_permissions.device_id = device_id; + updated_user_permissions.policy_app_id = application_id; + updated_user_permissions.consent_source = source; + + // Need to check to which app to send notification since maybe app registered + // from different device + SetUserConsentForApp(updated_user_permissions, + PolicyManager::kNotifyApplicationMode); + + PermissionConsent updated_external_consent_permissions; + updated_external_consent_permissions.group_permissions = + external_consent_groups_matches; + updated_external_consent_permissions.device_id = device_id; + updated_external_consent_permissions.policy_app_id = application_id; + updated_user_permissions.consent_source = source; + + cache_->SetExternalConsentForApp(updated_external_consent_permissions); +} + +void PolicyManagerImpl::NotifySystem( + const PolicyManagerImpl::AppPoliciesValueType& app_policy) const { + listener()->OnPendingPermissionChange(app_policy.first); +} + +void PolicyManagerImpl::SendPermissionsToApp( + const PolicyManagerImpl::AppPoliciesValueType& app_policy) { + const std::string app_id = app_policy.first; + + const std::string device_id = GetCurrentDeviceId(app_id); + if (device_id.empty()) { + LOG4CXX_WARN(logger_, + "Couldn't find device info for application id: " << app_id); + return; + } + std::vector<FunctionalGroupPermission> group_permissons; + GetPermissionsForApp(device_id, app_id, group_permissons); + + Permissions notification_data; + + // Need to get rid of this call + utils::SharedPtr<policy_table::Table> policy_table_snapshot = + cache_->GenerateSnapshot(); + + PrepareNotificationData( + policy_table_snapshot->policy_table.functional_groupings, + app_policy.second.groups, + group_permissons, + notification_data); + + LOG4CXX_INFO(logger_, "Send notification for application_id: " << app_id); + listener()->OnPermissionsUpdated( + app_id, + notification_data, + policy_table::EnumToJsonString(app_policy.second.default_hmi)); +} + +void PolicyManagerImpl::ProcessExternalConsentStatusUpdate( + const GroupsByExternalConsentStatus& groups_by_status, + const ConsentProcessingPolicy processing_policy) { + GroupsNames allowed_groups; + GroupsNames disallowed_groups; + CalculateGroupsConsentFromExternalConsent( + groups_by_status, allowed_groups, disallowed_groups); + + std::map<std::string, std::string> known_links = + cache_->GetKnownLinksFromPT(); + std::map<std::string, std::string> registered_links; + listener_->GetRegisteredLinks(registered_links); + + std::map<std::string, std::string> all_known; + std::merge(known_links.begin(), + known_links.end(), + registered_links.begin(), + registered_links.end(), + std::inserter(all_known, all_known.begin())); + + std::map<std::string, std::string>::const_iterator it_links = + all_known.begin(); + for (; all_known.end() != it_links; ++it_links) { + UpdateAppConsentWithExternalConsent(it_links->first, + it_links->second, + allowed_groups, + disallowed_groups, + processing_policy); + } +} + +bool ConsentStatusComparatorFunc(const ExternalConsentStatusItem& i1, + const ExternalConsentStatusItem& i2) { + return (i1.entity_id_ < i2.entity_id_) || + (i1.entity_type_ < i2.entity_type_) || (i1.status_ < i2.status_); +} + +bool PolicyManagerImpl::IsNeedToUpdateExternalConsentStatus( + const ExternalConsentStatus& new_status) const { + LOG4CXX_AUTO_TRACE(logger_); + typedef std::vector<ExternalConsentStatusItem> ItemV; + const ExternalConsentStatus existing_status = + cache_->GetExternalConsentEntities(); + + ItemV new_status_v(new_status.begin(), new_status.end()); + ItemV existing_status_v(existing_status.begin(), existing_status.end()); + + ItemV difference_v; + difference_v.resize(new_status_v.size() + existing_status_v.size()); + + ItemV::iterator ci = difference_v.begin(); + ci = std::set_difference(new_status_v.begin(), + new_status_v.end(), + existing_status_v.begin(), + existing_status_v.end(), + difference_v.begin(), + ConsentStatusComparatorFunc); + difference_v.resize(ci - difference_v.begin()); + + return !difference_v.empty(); +} + +bool PolicyManagerImpl::SetExternalConsentStatus( + const ExternalConsentStatus& status) { + LOG4CXX_AUTO_TRACE(logger_); + + if (status.empty()) { + LOG4CXX_INFO(logger_, "External consent status is empty, skipping update."); + return false; + } + + if (!cache_->SetExternalConsentStatus(status)) { + LOG4CXX_WARN(logger_, "Can't set external user consent status."); + return false; + } + + GroupsByExternalConsentStatus groups_by_status = + cache_->GetGroupsWithSameEntities(status); + ProcessExternalConsentStatusUpdate( + groups_by_status, ConsentProcessingPolicy::kExternalConsentBased); + + return true; +} + +ExternalConsentStatus PolicyManagerImpl::GetExternalConsentStatus() { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->GetExternalConsentStatus(); +} + +void PolicyManagerImpl::CalculateGroupsConsentFromExternalConsent( + const GroupsByExternalConsentStatus& groups_by_external_consent, + GroupsNames& out_allowed_groups, + GroupsNames& out_disallowed_groups) const { + LOG4CXX_AUTO_TRACE(logger_); + GroupSorter sorter(out_allowed_groups, out_disallowed_groups); + std::for_each(groups_by_external_consent.begin(), + groups_by_external_consent.end(), + sorter); + + GroupsNames filtered_allowed_groups; + std::set_difference( + out_allowed_groups.begin(), + out_allowed_groups.end(), + out_disallowed_groups.begin(), + out_disallowed_groups.end(), + std::inserter(filtered_allowed_groups, filtered_allowed_groups.begin())); + + out_allowed_groups = filtered_allowed_groups; +} + +bool PolicyManagerImpl::ExceededDays() { + LOG4CXX_AUTO_TRACE(logger_); + + TimevalStruct current_time = date_time::DateTime::getCurrentTime(); + const int kSecondsInDay = 60 * 60 * 24; + const int days = current_time.tv_sec / kSecondsInDay; + + DCHECK(std::numeric_limits<uint16_t>::max() >= days); + + if (std::numeric_limits<uint16_t>::max() <= days) { + LOG4CXX_WARN(logger_, "The days since epoch exceeds maximum value."); + return false; + } + return 0 == cache_->DaysBeforeExchange(static_cast<uint16_t>(days)); +} + +void PolicyManagerImpl::KmsChanged(int kilometers) { + LOG4CXX_AUTO_TRACE(logger_); + if (0 == cache_->KilometersBeforeExchange(kilometers)) { + LOG4CXX_INFO(logger_, "Enough kilometers passed to send for PT update."); + update_status_manager_.ScheduleUpdate(); + StartPTExchange(); + PTUpdatedAt(KILOMETERS, kilometers); + } +} + +void PolicyManagerImpl::IncrementIgnitionCycles() { + cache_->IncrementIgnitionCycles(); +} + +std::string PolicyManagerImpl::ForcePTExchange() { + update_status_manager_.ScheduleUpdate(); + StartPTExchange(); + return update_status_manager_.StringifiedUpdateStatus(); +} + +std::string PolicyManagerImpl::ForcePTExchangeAtUserRequest() { + LOG4CXX_AUTO_TRACE(logger_); + update_status_manager_.ScheduleManualUpdate(); + StartPTExchange(); + return update_status_manager_.StringifiedUpdateStatus(); +} + +std::string PolicyManagerImpl::GetPolicyTableStatus() const { + return update_status_manager_.StringifiedUpdateStatus(); +} + +int PolicyManagerImpl::NextRetryTimeout() { + sync_primitives::AutoLock auto_lock(retry_sequence_lock_); + LOG4CXX_DEBUG(logger_, "Index: " << retry_sequence_index_); + int next = 0; + if (!retry_sequence_seconds_.empty() && + retry_sequence_index_ < retry_sequence_seconds_.size()) { + next = retry_sequence_seconds_[retry_sequence_index_]; + ++retry_sequence_index_; + } + return next; +} + +void PolicyManagerImpl::RefreshRetrySequence() { + sync_primitives::AutoLock auto_lock(retry_sequence_lock_); + retry_sequence_timeout_ = cache_->TimeoutResponse(); + retry_sequence_seconds_.clear(); + cache_->SecondsBetweenRetries(retry_sequence_seconds_); +} + +void PolicyManagerImpl::ResetRetrySequence() { + sync_primitives::AutoLock auto_lock(retry_sequence_lock_); + retry_sequence_index_ = 0; + update_status_manager_.OnResetRetrySequence(); +} + +uint32_t PolicyManagerImpl::TimeoutExchangeMSec() { + return retry_sequence_timeout_; +} + +const std::vector<int> PolicyManagerImpl::RetrySequenceDelaysSeconds() { + sync_primitives::AutoLock auto_lock(retry_sequence_lock_); + return retry_sequence_seconds_; +} + +void PolicyManagerImpl::OnExceededTimeout() { + update_status_manager_.OnUpdateTimeoutOccurs(); +} + +void PolicyManagerImpl::OnUpdateStarted() { + uint32_t update_timeout = TimeoutExchangeMSec(); + LOG4CXX_DEBUG(logger_, + "Update timeout will be set to (milisec): " << update_timeout); + send_on_update_sent_out_ = + !wrong_ptu_update_received_ && !update_status_manager_.IsUpdatePending(); + + if (send_on_update_sent_out_) { + update_status_manager_.OnUpdateSentOut(update_timeout); + } + cache_->SaveUpdateRequired(true); +} + +void PolicyManagerImpl::PTUpdatedAt(Counters counter, int value) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->SetCountersPassedForSuccessfulUpdate(counter, value); + cache_->ResetIgnitionCycles(); +} + +void PolicyManagerImpl::Increment(usage_statistics::GlobalCounterId type) { + LOG4CXX_INFO(logger_, "Increment without app id"); + cache_->Increment(type); +} + +void PolicyManagerImpl::Increment(const std::string& app_id, + usage_statistics::AppCounterId type) { + LOG4CXX_DEBUG(logger_, "Increment " << app_id << " AppCounter: " << type); + cache_->Increment(app_id, type); +} + +void PolicyManagerImpl::Set(const std::string& app_id, + usage_statistics::AppInfoId type, + const std::string& value) { + LOG4CXX_INFO(logger_, "Set " << app_id); + cache_->Set(app_id, type, value); +} + +void PolicyManagerImpl::Add(const std::string& app_id, + usage_statistics::AppStopwatchId type, + int32_t timespan_seconds) { + LOG4CXX_INFO(logger_, "Add " << app_id); + cache_->Add(app_id, type, timespan_seconds); +} + +bool PolicyManagerImpl::IsApplicationRevoked(const std::string& app_id) const { + return cache_->IsApplicationRevoked(app_id); +} + +bool PolicyManagerImpl::IsConsentNeeded(const std::string& app_id) { + LOG4CXX_AUTO_TRACE(logger_); + const std::string device_id = GetCurrentDeviceId(app_id); + int count = cache_->CountUnconsentedGroups(app_id, device_id); + LOG4CXX_DEBUG(logger_, "There are: " << count << " unconsented groups."); + return count != 0; +} + +void PolicyManagerImpl::SetVINValue(const std::string& value) { + cache_->SetVINValue(value); +} + +AppPermissions PolicyManagerImpl::GetAppPermissionsChanges( + const std::string& policy_app_id) { + PendingPermissions::iterator app_id_diff = + app_permissions_diff_.find(policy_app_id); + + AppPermissions permissions(policy_app_id); + + if (app_permissions_diff_.end() != app_id_diff) { + permissions = app_id_diff->second; + } else { + permissions.appPermissionsConsentNeeded = IsConsentNeeded(policy_app_id); + permissions.appRevoked = IsApplicationRevoked(policy_app_id); + GetPriority(permissions.application_id, &permissions.priority); + } + return permissions; +} + +void PolicyManagerImpl::RemovePendingPermissionChanges( + const std::string& app_id) { + app_permissions_diff_.erase(app_id); +} + +bool PolicyManagerImpl::CanAppKeepContext(const std::string& app_id) const { + return cache_->CanAppKeepContext(app_id); +} + +bool PolicyManagerImpl::CanAppStealFocus(const std::string& app_id) const { + return cache_->CanAppStealFocus(app_id); +} + +void PolicyManagerImpl::MarkUnpairedDevice(const std::string& device_id) { + if (!cache_->SetUnpairedDevice(device_id)) { + LOG4CXX_DEBUG(logger_, "Could not set unpaired flag for " << device_id); + return; + } + SetUserConsentForDevice(device_id, false); +} + +void PolicyManagerImpl::OnAppRegisteredOnMobile( + const std::string& application_id) { + StartPTExchange(); + SendNotificationOnPermissionsUpdated(application_id); +} + +const MetaInfo PolicyManagerImpl::GetMetaInfo() const { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->GetMetaInfo(); +} + +std::string PolicyManagerImpl::RetrieveCertificate() const { + LOG4CXX_AUTO_TRACE(logger_); + return cache_->GetCertificate(); +} + +bool PolicyManagerImpl::HasCertificate() const { + return !cache_->GetCertificate().empty(); +} + +void PolicyManagerImpl::SetDecryptedCertificate( + const std::string& certificate) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->SetDecryptedCertificate(certificate); +} + +AppIdURL PolicyManagerImpl::GetNextUpdateUrl(const EndpointUrls& urls) { + LOG4CXX_AUTO_TRACE(logger_); + + const AppIdURL next_app_url = RetrySequenceUrl(retry_sequence_url_, urls); + + retry_sequence_url_.url_idx_ = next_app_url.second + 1; + retry_sequence_url_.app_idx_ = next_app_url.first; + retry_sequence_url_.policy_app_id_ = urls[next_app_url.first].app_id; + + return next_app_url; +} + +AppIdURL PolicyManagerImpl::RetrySequenceUrl(const struct RetrySequenceURL& rs, + const EndpointUrls& urls) const { + uint32_t url_idx = rs.url_idx_; + uint32_t app_idx = rs.app_idx_; + const std::string& app_id = rs.policy_app_id_; + + if (urls.size() <= app_idx) { + // Index of current application doesn't exist any more due to app(s) + // unregistration + url_idx = 0; + app_idx = 0; + } else if (urls[app_idx].app_id != app_id) { + // Index of current application points to another one due to app(s) + // registration/unregistration + url_idx = 0; + } else if (url_idx >= urls[app_idx].url.size()) { + // Index of current application is OK, but all of its URL are sent, + // move to the next application + url_idx = 0; + if (++app_idx >= urls.size()) { + app_idx = 0; + } + } + const AppIdURL next_app_url = std::make_pair(app_idx, url_idx); + + return next_app_url; +} + +/** + * @brief The CallStatusChange class notify update manager aboun new application + */ +class CallStatusChange : public utils::Callable { + public: + CallStatusChange(UpdateStatusManager& upd_manager, + const DeviceConsent& device_consent) + : upd_manager_(upd_manager), device_consent_(device_consent) {} + + // Callable interface + void operator()() const { + upd_manager_.OnNewApplicationAdded(device_consent_); + } + + private: + UpdateStatusManager& upd_manager_; + const DeviceConsent device_consent_; +}; + +StatusNotifier PolicyManagerImpl::AddApplication( + const std::string& application_id, + const rpc::policy_table_interface_base::AppHmiTypes& hmi_types) { + LOG4CXX_AUTO_TRACE(logger_); + const std::string device_id = GetCurrentDeviceId(application_id); + DeviceConsent device_consent = GetUserConsentForDevice(device_id); + sync_primitives::AutoLock lock(apps_registration_lock_); + if (IsNewApplication(application_id)) { + AddNewApplication(application_id, device_consent); + return utils::MakeShared<CallStatusChange>(update_status_manager_, + device_consent); + } else { + PromoteExistedApplication(application_id, device_consent); + if (helpers::in_range(hmi_types, policy_table::AHT_NAVIGATION) && + !HasCertificate()) { + LOG4CXX_DEBUG(logger_, "Certificate does not exist, scheduling update."); + update_status_manager_.ScheduleUpdate(); + } + return utils::MakeShared<utils::CallNothing>(); + } +} + +void PolicyManagerImpl::RemoveAppConsentForGroup( + const std::string& app_id, const std::string& group_name) { + cache_->RemoveAppConsentForGroup(app_id, group_name); +} + +bool PolicyManagerImpl::IsPredataPolicy( + const std::string& policy_app_id) const { + LOG4CXX_INFO(logger_, "IsPredataApp"); + return cache_->IsPredataPolicy(policy_app_id); +} + +void PolicyManagerImpl::ProcessExternalConsentStatusForApp( + const std::string& application_id, + const ConsentProcessingPolicy processing_policy) { + ExternalConsentStatus status = cache_->GetExternalConsentStatus(); + GroupsByExternalConsentStatus groups_by_status = + cache_->GetGroupsWithSameEntities(status); + + GroupsNames allowed_groups; + GroupsNames disallowed_groups; + CalculateGroupsConsentFromExternalConsent( + groups_by_status, allowed_groups, disallowed_groups); + + const std::string device_id = GetCurrentDeviceId(application_id); + UpdateAppConsentWithExternalConsent(device_id, + application_id, + allowed_groups, + disallowed_groups, + processing_policy); +} + +void PolicyManagerImpl::AddNewApplication(const std::string& application_id, + DeviceConsent device_consent) { + LOG4CXX_AUTO_TRACE(logger_); + + if (kDeviceHasNoConsent == device_consent || + kDeviceDisallowed == device_consent) { + LOG4CXX_INFO(logger_, + "Setting " + << policy::kPreDataConsentId + << " permissions for application id: " << application_id); + cache_->SetPredataPolicy(application_id); + } else { + LOG4CXX_INFO(logger_, + "Setting " + << policy::kDefaultId + << " permissions for application id: " << application_id); + cache_->SetDefaultPolicy(application_id); + } + + ProcessExternalConsentStatusForApp( + application_id, ConsentProcessingPolicy::kExternalConsentBased); +} + +void PolicyManagerImpl::PromoteExistedApplication( + const std::string& application_id, DeviceConsent device_consent) { + // If device consent changed to allowed during application being + // disconnected, app permissions should be changed also + if (kDeviceAllowed == device_consent && + cache_->IsPredataPolicy(application_id)) { + cache_->SetDefaultPolicy(application_id); + } + ProcessExternalConsentStatusForApp(application_id, + ConsentProcessingPolicy::kTimestampBased); +} + +bool PolicyManagerImpl::IsNewApplication( + const std::string& application_id) const { + return false == cache_->IsApplicationRepresented(application_id); +} + +bool PolicyManagerImpl::ResetPT(const std::string& file_name) { + LOG4CXX_AUTO_TRACE(logger_); + cache_->ResetCalculatedPermissions(); + const bool result = cache_->ResetPT(file_name); + if (result) { + RefreshRetrySequence(); + } + return result; +} + +bool PolicyManagerImpl::CheckAppStorageFolder() const { + LOG4CXX_AUTO_TRACE(logger_); + const std::string app_storage_folder = get_settings().app_storage_folder(); + LOG4CXX_DEBUG(logger_, "AppStorageFolder " << app_storage_folder); + if (!file_system::DirectoryExists(app_storage_folder)) { + LOG4CXX_WARN(logger_, + "Storage directory doesn't exist " << app_storage_folder); + return false; + } + if (!(file_system::IsWritingAllowed(app_storage_folder) && + file_system::IsReadingAllowed(app_storage_folder))) { + LOG4CXX_WARN(logger_, + "Storage directory doesn't have read/write permissions " + << app_storage_folder); + return false; + } + return true; +} + +bool PolicyManagerImpl::InitPT(const std::string& file_name, + const PolicySettings* settings) { + LOG4CXX_AUTO_TRACE(logger_); + settings_ = settings; + if (!CheckAppStorageFolder()) { + LOG4CXX_ERROR(logger_, "Can not read/write into AppStorageFolder"); + return false; + } + const bool ret = cache_->Init(file_name, settings); + if (ret) { + RefreshRetrySequence(); + update_status_manager_.OnPolicyInit(cache_->UpdateRequired()); + } + return ret; +} + +uint32_t PolicyManagerImpl::HeartBeatTimeout(const std::string& app_id) const { + return cache_->HeartBeatTimeout(app_id); +} + +void PolicyManagerImpl::SaveUpdateStatusRequired(bool is_update_needed) { + cache_->SaveUpdateRequired(is_update_needed); +} + +void PolicyManagerImpl::set_cache_manager( + CacheManagerInterface* cache_manager) { + cache_ = cache_manager; +} + +} // namespace policy |