| // |
| // Copyright (C) 2015 The Android Open Source Project |
| // |
| // 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. |
| // |
| #include <time.h> |
| |
| #include <base/message_loop/message_loop.h> |
| |
| #include "proxy_dbus_client.h" |
| |
| const char ProxyDbusClient::kCommonLogScopes[] = |
| "connection+dbus+device+link+manager+portal+service"; |
| const int ProxyDbusClient::kLogLevel = -4; |
| const char ProxyDbusClient::kDbusErrorObjectUnknown[] = |
| "org.freedesktop.DBus.Error.UnknownObject"; |
| |
| namespace { |
| template<typename Proxy> bool GetPropertyValueFromProxy( |
| Proxy* proxy, |
| const std::string& property_name, |
| brillo::Any* property_value) { |
| CHECK(property_value); |
| brillo::VariantDictionary proxy_properties; |
| brillo::ErrorPtr error; |
| CHECK(proxy->GetProperties(&proxy_properties, &error)); |
| if (proxy_properties.find(property_name) == proxy_properties.end()) { |
| return false; |
| } |
| *property_value = proxy_properties[property_name]; |
| return true; |
| } |
| |
| template<typename Proxy> void IsProxyPropertyValueIn( |
| Proxy* proxy, |
| const std::string& property_name, |
| const std::vector<brillo::Any>& expected_values, |
| base::Time wait_start_time, |
| bool* is_success, |
| brillo::Any* final_value, |
| long* elapsed_time_milliseconds) { |
| brillo::Any property_value; |
| *is_success = false; |
| if ((GetPropertyValueFromProxy<Proxy>(proxy, property_name, &property_value)) && |
| (std::find(expected_values.begin(), expected_values.end(), |
| property_value) != expected_values.end())) { |
| *is_success = true; |
| } |
| if (final_value) { |
| *final_value = property_value; |
| } |
| if (elapsed_time_milliseconds) { |
| *elapsed_time_milliseconds = |
| (base::Time::Now() - wait_start_time).InMilliseconds(); |
| } |
| } |
| |
| // This is invoked when the dbus detects a change in one of |
| // the properties of the proxy. We need to check if the property |
| // we're interested in has reached one of the expected values. |
| void PropertyChangedSignalCallback( |
| const std::string& watched_property_name, |
| const std::vector<brillo::Any>& expected_values, |
| const std::string& changed_property_name, |
| const brillo::Any& new_property_value) { |
| if ((watched_property_name == changed_property_name) && |
| (std::find(expected_values.begin(), expected_values.end(), |
| new_property_value) != expected_values.end())) { |
| // Unblock the waiting function by stopping the message loop. |
| base::MessageLoop::current()->QuitNow(); |
| } |
| } |
| |
| // This is invoked to indicate whether dbus successfully connected our |
| // signal callback or not. |
| void PropertyChangedOnConnectedCallback( |
| const std::string& /* watched_property_name */, |
| const std::string& /* interface */, |
| const std::string& /* signal_name */, |
| bool success) { |
| CHECK(success); |
| } |
| |
| template<typename Proxy> |
| void HelpRegisterPropertyChangedSignalHandler( |
| Proxy* proxy, |
| dbus::ObjectProxy::OnConnectedCallback on_connected_callback, |
| const DbusPropertyChangeCallback& signal_callback) { |
| // Re-order |on_connected_callback| and |signal_callback|, to meet |
| // the requirements of RegisterPropertyChangedSignalHandler(). |
| proxy->RegisterPropertyChangedSignalHandler( |
| signal_callback, on_connected_callback); |
| } |
| |
| template<typename OutValueType, typename ConditionChangeCallbackType> |
| void WaitForCondition( |
| base::Callback<void(base::Time, bool*, OutValueType*, long*)> |
| condition_termination_checker, |
| base::Callback<ConditionChangeCallbackType> condition_change_callback, |
| base::Callback<void(const base::Callback<ConditionChangeCallbackType>&)> |
| condition_change_callback_registrar, |
| long timeout_milliseconds, |
| bool* is_success, |
| OutValueType* out_value, |
| long* elapsed_time_milliseconds) { |
| CHECK(is_success); |
| const base::Time wait_start_time(base::Time::Now()); |
| const base::TimeDelta timeout( |
| base::TimeDelta::FromMilliseconds(timeout_milliseconds)); |
| base::CancelableClosure wait_timeout_callback; |
| base::CancelableCallback<ConditionChangeCallbackType> change_callback; |
| |
| condition_termination_checker.Run( |
| wait_start_time, is_success, out_value, elapsed_time_milliseconds); |
| if (*is_success) { |
| return; |
| } |
| |
| wait_timeout_callback.Reset(base::MessageLoop::QuitWhenIdleClosure()); |
| change_callback.Reset(condition_change_callback); |
| |
| condition_change_callback_registrar.Run(change_callback.callback()); |
| |
| // Add timeout, in case we never hit the expected condition. |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| wait_timeout_callback.callback(), |
| timeout); |
| |
| // Wait for the condition to occur within |timeout_milliseconds|. |
| base::MessageLoop::current()->Run(); |
| |
| wait_timeout_callback.Cancel(); |
| change_callback.Cancel(); |
| |
| // We could have reached here either because we timed out or |
| // because we reached the condition. |
| condition_termination_checker.Run( |
| wait_start_time, is_success, out_value, elapsed_time_milliseconds); |
| } |
| } // namespace |
| |
| ProxyDbusClient::ProxyDbusClient(scoped_refptr<dbus::Bus> bus) |
| : dbus_bus_(bus), |
| shill_manager_proxy_(dbus_bus_), |
| weak_ptr_factory_(this) { |
| } |
| |
| void ProxyDbusClient::SetLogging(Technology tech) { |
| std::string log_scopes(kCommonLogScopes); |
| switch (tech) { |
| case TECHNOLOGY_CELLULAR: |
| log_scopes += "+cellular"; |
| break; |
| case TECHNOLOGY_ETHERNET: |
| log_scopes += "+ethernet"; |
| break; |
| case TECHNOLOGY_VPN: |
| log_scopes += "+vpn"; |
| break; |
| case TECHNOLOGY_WIFI: |
| log_scopes += "+wifi"; |
| break; |
| case TECHNOLOGY_WIMAX: |
| log_scopes += "+wimax"; |
| break; |
| } |
| SetLoggingInternal(kLogLevel, log_scopes); |
| } |
| |
| std::vector<std::unique_ptr<DeviceProxy>> ProxyDbusClient::GetDeviceProxies() { |
| return GetProxies<DeviceProxy>(shill::kDevicesProperty); |
| } |
| |
| std::vector<std::unique_ptr<ServiceProxy>> ProxyDbusClient::GetServiceProxies() { |
| return GetProxies<ServiceProxy>(shill::kServicesProperty); |
| } |
| |
| std::vector<std::unique_ptr<ProfileProxy>> ProxyDbusClient::GetProfileProxies() { |
| return GetProxies<ProfileProxy>(shill::kProfilesProperty); |
| } |
| |
| std::unique_ptr<DeviceProxy> ProxyDbusClient::GetMatchingDeviceProxy( |
| const brillo::VariantDictionary& expected_properties) { |
| return GetMatchingProxy<DeviceProxy>(shill::kDevicesProperty, expected_properties); |
| } |
| |
| std::unique_ptr<ServiceProxy> ProxyDbusClient::GetMatchingServiceProxy( |
| const brillo::VariantDictionary& expected_properties) { |
| return GetMatchingProxy<ServiceProxy>(shill::kServicesProperty, expected_properties); |
| } |
| |
| std::unique_ptr<ProfileProxy> ProxyDbusClient::GetMatchingProfileProxy( |
| const brillo::VariantDictionary& expected_properties) { |
| return GetMatchingProxy<ProfileProxy>(shill::kProfilesProperty, expected_properties); |
| } |
| |
| bool ProxyDbusClient::GetPropertyValueFromDeviceProxy( |
| DeviceProxy* proxy, |
| const std::string& property_name, |
| brillo::Any* property_value) { |
| return GetPropertyValueFromProxy<DeviceProxy>( |
| proxy, property_name, property_value); |
| } |
| |
| bool ProxyDbusClient::GetPropertyValueFromServiceProxy( |
| ServiceProxy* proxy, |
| const std::string& property_name, |
| brillo::Any* property_value) { |
| return GetPropertyValueFromProxy<ServiceProxy>( |
| proxy, property_name, property_value); |
| } |
| |
| bool ProxyDbusClient::GetPropertyValueFromProfileProxy( |
| ProfileProxy* proxy, |
| const std::string& property_name, |
| brillo::Any* property_value) { |
| return GetPropertyValueFromProxy<ProfileProxy>( |
| proxy, property_name, property_value); |
| } |
| |
| bool ProxyDbusClient::WaitForDeviceProxyPropertyValueIn( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name, |
| const std::vector<brillo::Any>& expected_values, |
| long timeout_milliseconds, |
| brillo::Any* final_value, |
| long* elapsed_time_milliseconds) { |
| return WaitForProxyPropertyValueIn<DeviceProxy>( |
| object_path, property_name, expected_values, timeout_milliseconds, |
| final_value, elapsed_time_milliseconds); |
| } |
| |
| bool ProxyDbusClient::WaitForServiceProxyPropertyValueIn( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name, |
| const std::vector<brillo::Any>& expected_values, |
| long timeout_milliseconds, |
| brillo::Any* final_value, |
| long* elapsed_time_milliseconds) { |
| return WaitForProxyPropertyValueIn<ServiceProxy>( |
| object_path, property_name, expected_values, timeout_milliseconds, |
| final_value, elapsed_time_milliseconds); |
| } |
| |
| bool ProxyDbusClient::WaitForProfileProxyPropertyValueIn( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name, |
| const std::vector<brillo::Any>& expected_values, |
| long timeout_milliseconds, |
| brillo::Any* final_value, |
| long* elapsed_time_milliseconds) { |
| return WaitForProxyPropertyValueIn<ProfileProxy>( |
| object_path, property_name, expected_values, timeout_milliseconds, |
| final_value, elapsed_time_milliseconds); |
| } |
| |
| std::unique_ptr<ServiceProxy> ProxyDbusClient::GetServiceProxy( |
| const brillo::VariantDictionary& expected_properties) { |
| dbus::ObjectPath service_path; |
| brillo::ErrorPtr error; |
| if (!shill_manager_proxy_.GetService( |
| expected_properties, &service_path, &error)) { |
| return nullptr; |
| } |
| return std::unique_ptr<ServiceProxy>( |
| new ServiceProxy(dbus_bus_, service_path)); |
| } |
| |
| std::unique_ptr<ProfileProxy> ProxyDbusClient::GetActiveProfileProxy() { |
| return GetProxyForObjectPath<ProfileProxy>(GetObjectPathForActiveProfile()); |
| } |
| |
| std::unique_ptr<ServiceProxy> ProxyDbusClient::WaitForMatchingServiceProxy( |
| const brillo::VariantDictionary& service_properties, |
| const std::string& service_type, |
| long timeout_milliseconds, |
| int rescan_interval_milliseconds, |
| long* elapsed_time_milliseconds) { |
| auto condition_termination_checker = |
| base::Bind(&ProxyDbusClient::IsMatchingServicePresent, |
| weak_ptr_factory_.GetWeakPtr(), |
| service_properties); |
| auto condition_change_callback = |
| base::Bind(&ProxyDbusClient::FindServiceOrRestartScan, |
| weak_ptr_factory_.GetWeakPtr(), |
| service_properties, |
| service_type); |
| auto condition_change_callback_registrar = |
| base::Bind(&ProxyDbusClient::InitiateScanForService, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::TimeDelta::FromMilliseconds(rescan_interval_milliseconds), |
| service_type); |
| |
| std::unique_ptr<ServiceProxy> service_proxy; |
| bool is_success; |
| WaitForCondition( |
| condition_termination_checker, condition_change_callback, |
| condition_change_callback_registrar, |
| timeout_milliseconds, &is_success, &service_proxy, elapsed_time_milliseconds); |
| return service_proxy; |
| } |
| |
| bool ProxyDbusClient::ConfigureService( |
| const brillo::VariantDictionary& config_params) { |
| dbus::ObjectPath service_path; |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.ConfigureService( |
| config_params, &service_path, &error); |
| } |
| |
| bool ProxyDbusClient::ConfigureServiceByGuid( |
| const std::string& guid, |
| const brillo::VariantDictionary& config_params) { |
| dbus::ObjectPath service_path; |
| brillo::ErrorPtr error; |
| brillo::VariantDictionary guid_config_params(config_params); |
| guid_config_params[shill::kGuidProperty] = guid; |
| return shill_manager_proxy_.ConfigureService( |
| guid_config_params, &service_path, &error); |
| } |
| |
| bool ProxyDbusClient::ConnectService( |
| const dbus::ObjectPath& object_path, |
| long timeout_milliseconds) { |
| auto proxy = GetProxyForObjectPath<ServiceProxy>(object_path); |
| brillo::ErrorPtr error; |
| if (!proxy->Connect(&error)) { |
| return false; |
| } |
| const std::vector<brillo::Any> expected_values = { |
| brillo::Any(std::string(shill::kStatePortal)), |
| brillo::Any(std::string(shill::kStateOnline)) }; |
| return WaitForProxyPropertyValueIn<ServiceProxy>( |
| object_path, shill::kStateProperty, expected_values, |
| timeout_milliseconds, nullptr, nullptr); |
| } |
| |
| bool ProxyDbusClient::DisconnectService( |
| const dbus::ObjectPath& object_path, |
| long timeout_milliseconds) { |
| auto proxy = GetProxyForObjectPath<ServiceProxy>(object_path); |
| brillo::ErrorPtr error; |
| if (!proxy->Disconnect(&error)) { |
| return false; |
| } |
| const std::vector<brillo::Any> expected_values = { |
| brillo::Any(std::string(shill::kStateIdle)) }; |
| return WaitForProxyPropertyValueIn<ServiceProxy>( |
| object_path, shill::kStateProperty, expected_values, |
| timeout_milliseconds, nullptr, nullptr); |
| } |
| |
| bool ProxyDbusClient::CreateProfile(const std::string& profile_name) { |
| dbus::ObjectPath profile_path; |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.CreateProfile( |
| profile_name, &profile_path, &error); |
| } |
| |
| bool ProxyDbusClient::RemoveProfile(const std::string& profile_name) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.RemoveProfile(profile_name, &error); |
| } |
| |
| bool ProxyDbusClient::PushProfile(const std::string& profile_name) { |
| dbus::ObjectPath profile_path; |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.PushProfile( |
| profile_name, &profile_path, &error); |
| } |
| |
| bool ProxyDbusClient::PopProfile(const std::string& profile_name) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.PopProfile(profile_name, &error); |
| } |
| |
| bool ProxyDbusClient::PopAnyProfile() { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.PopAnyProfile(&error); |
| } |
| |
| bool ProxyDbusClient::RequestServiceScan(const std::string& service_type) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.RequestScan(service_type, &error); |
| } |
| |
| bool ProxyDbusClient::GetServiceOrder(std::string* order) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.GetServiceOrder(order, &error); |
| } |
| |
| bool ProxyDbusClient::SetServiceOrder(const std::string& order) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.SetServiceOrder(order, &error); |
| } |
| |
| bool ProxyDbusClient::SetSchedScan(bool enable) { |
| brillo::ErrorPtr error; |
| return shill_manager_proxy_.SetSchedScan(enable, &error); |
| } |
| |
| bool ProxyDbusClient::GetPropertyValueFromManager( |
| const std::string& property_name, |
| brillo::Any* property_value) { |
| return GetPropertyValueFromProxy( |
| &shill_manager_proxy_, property_name, property_value); |
| } |
| |
| dbus::ObjectPath ProxyDbusClient::GetObjectPathForActiveProfile() { |
| brillo::Any property_value; |
| if (!GetPropertyValueFromManager( |
| shill::kActiveProfileProperty, &property_value)) { |
| return dbus::ObjectPath(); |
| } |
| return dbus::ObjectPath(property_value.Get<std::string>()); |
| } |
| |
| bool ProxyDbusClient::SetLoggingInternal(int level, const std::string& tags) { |
| bool is_success = true; |
| brillo::ErrorPtr error; |
| is_success &= shill_manager_proxy_.SetDebugLevel(level, &error); |
| is_success &= shill_manager_proxy_.SetDebugTags(tags, &error); |
| return is_success; |
| } |
| |
| template<typename Proxy> |
| std::unique_ptr<Proxy> ProxyDbusClient::GetProxyForObjectPath( |
| const dbus::ObjectPath& object_path) { |
| return std::unique_ptr<Proxy>(new Proxy(dbus_bus_, object_path)); |
| } |
| |
| // Templated functions to return the object path property_name based on |
| template<typename Proxy> |
| std::vector<std::unique_ptr<Proxy>> ProxyDbusClient::GetProxies( |
| const std::string& object_paths_property_name) { |
| brillo::Any object_paths; |
| if (!GetPropertyValueFromManager(object_paths_property_name, &object_paths)) { |
| return std::vector<std::unique_ptr<Proxy>>(); |
| } |
| std::vector<std::unique_ptr<Proxy>> proxies; |
| for (const auto& object_path : |
| object_paths.Get<std::vector<dbus::ObjectPath>>()) { |
| proxies.emplace_back(GetProxyForObjectPath<Proxy>(object_path)); |
| } |
| return proxies; |
| } |
| |
| template<typename Proxy> |
| std::unique_ptr<Proxy> ProxyDbusClient::GetMatchingProxy( |
| const std::string& object_paths_property_name, |
| const brillo::VariantDictionary& expected_properties) { |
| for (auto& proxy : GetProxies<Proxy>(object_paths_property_name)) { |
| brillo::VariantDictionary proxy_properties; |
| brillo::ErrorPtr error; |
| if (!proxy->GetProperties(&proxy_properties, &error)) { |
| // Ignore unknown object path errors since we might be using some proxies |
| // for objects which may have been destroyed since. |
| CHECK(error->GetCode() == kDbusErrorObjectUnknown); |
| continue; |
| } |
| bool all_expected_properties_matched = true; |
| for (const auto& expected_property : expected_properties) { |
| if (proxy_properties[expected_property.first] != expected_property.second) { |
| all_expected_properties_matched = false; |
| break; |
| } |
| } |
| if (all_expected_properties_matched) { |
| return std::move(proxy); |
| } |
| } |
| return nullptr; |
| } |
| |
| template<typename Proxy> |
| bool ProxyDbusClient::WaitForProxyPropertyValueIn( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name, |
| const std::vector<brillo::Any>& expected_values, |
| long timeout_milliseconds, |
| brillo::Any* final_value, |
| long* elapsed_time_milliseconds) { |
| // Creates a local proxy using |object_path| instead of accepting the proxy |
| // from the caller since we cannot deregister the signal property change |
| // callback associated. |
| auto proxy = GetProxyForObjectPath<Proxy>(object_path); |
| auto condition_termination_checker = |
| base::Bind(&IsProxyPropertyValueIn<Proxy>, |
| proxy.get(), |
| property_name, |
| expected_values); |
| auto condition_change_callback = |
| base::Bind(&PropertyChangedSignalCallback, |
| property_name, |
| expected_values); |
| auto condition_change_callback_registrar = |
| base::Bind(&HelpRegisterPropertyChangedSignalHandler<Proxy>, |
| base::Unretained(proxy.get()), |
| base::Bind(&PropertyChangedOnConnectedCallback, |
| property_name)); |
| bool is_success; |
| WaitForCondition( |
| condition_termination_checker, condition_change_callback, |
| condition_change_callback_registrar, |
| timeout_milliseconds, &is_success, final_value, elapsed_time_milliseconds); |
| return is_success; |
| } |
| |
| void ProxyDbusClient::IsMatchingServicePresent( |
| const brillo::VariantDictionary& service_properties, |
| base::Time wait_start_time, |
| bool* is_success, |
| std::unique_ptr<ServiceProxy>* service_proxy_out, |
| long* elapsed_time_milliseconds) { |
| auto service_proxy = GetMatchingServiceProxy(service_properties); |
| *is_success = false; |
| if (service_proxy) { |
| *is_success = true; |
| } |
| if (service_proxy_out) { |
| *service_proxy_out = std::move(service_proxy); |
| } |
| if (elapsed_time_milliseconds) { |
| *elapsed_time_milliseconds = |
| (base::Time::Now() - wait_start_time).InMilliseconds(); |
| } |
| } |
| |
| void ProxyDbusClient::FindServiceOrRestartScan( |
| const brillo::VariantDictionary& service_properties, |
| const std::string& service_type) { |
| if (GetMatchingServiceProxy(service_properties)) { |
| base::MessageLoop::current()->QuitNow(); |
| } else { |
| RestartScanForService(service_type); |
| } |
| } |
| |
| void ProxyDbusClient::InitiateScanForService( |
| base::TimeDelta rescan_interval, |
| const std::string& service_type, |
| const base::Closure& timer_callback) { |
| // Create a new timer instance for repeatedly calling the provided |
| // |timer_callback|. |WaitForCondition| will cancel |timer_callback|'s |
| // enclosing CancelableCallback when it exits and hence we need to |
| // use the same reference when we repeatedly schedule |timer_callback|. |
| wait_for_service_timer_.reset( |
| new base::Timer(FROM_HERE, rescan_interval, timer_callback, false)); |
| RestartScanForService(service_type); |
| } |
| |
| void ProxyDbusClient::RestartScanForService( |
| const std::string& service_type) { |
| RequestServiceScan(service_type); |
| wait_for_service_timer_->Reset(); |
| } |