blob: 448e74f3aa90b1c76ac886bebba007c1cf6fa17d [file] [log] [blame]
//
// 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();
}