//
// Copyright (C) 2014 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 "update_engine/update_manager/real_shill_provider.h"
#include <string>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <brillo/type_name_undecorate.h>
#include <shill/dbus-constants.h>
#include <shill/dbus-proxies.h>
using org::chromium::flimflam::ManagerProxyInterface;
using org::chromium::flimflam::ServiceProxyInterface;
using std::string;
namespace chromeos_update_manager {
ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) {
if (type_str == shill::kTypeEthernet) {
return ConnectionType::kEthernet;
} else if (type_str == shill::kTypeWifi) {
return ConnectionType::kWifi;
} else if (type_str == shill::kTypeWimax) {
return ConnectionType::kWimax;
} else if (type_str == shill::kTypeBluetooth) {
return ConnectionType::kBluetooth;
} else if (type_str == shill::kTypeCellular) {
return ConnectionType::kCellular;
}
return ConnectionType::kUnknown;
}
ConnectionTethering RealShillProvider::ParseConnectionTethering(
const string& tethering_str) {
if (tethering_str == shill::kTetheringNotDetectedState) {
return ConnectionTethering::kNotDetected;
} else if (tethering_str == shill::kTetheringSuspectedState) {
return ConnectionTethering::kSuspected;
} else if (tethering_str == shill::kTetheringConfirmedState) {
return ConnectionTethering::kConfirmed;
}
return ConnectionTethering::kUnknown;
}
bool RealShillProvider::Init() {
ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
if (!manager_proxy)
return false;
// Subscribe to the manager's PropertyChanged signal.
manager_proxy->RegisterPropertyChangedSignalHandler(
base::Bind(&RealShillProvider::OnManagerPropertyChanged,
base::Unretained(this)),
base::Bind(&RealShillProvider::OnSignalConnected,
base::Unretained(this)));
// Attempt to read initial connection status. Even if this fails because shill
// is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
// signal as soon as it comes up, so this is not a critical step.
brillo::VariantDictionary properties;
brillo::ErrorPtr error;
if (!manager_proxy->GetProperties(&properties, &error))
return true;
const auto& prop_default_service =
properties.find(shill::kDefaultServiceProperty);
if (prop_default_service != properties.end()) {
OnManagerPropertyChanged(prop_default_service->first,
prop_default_service->second);
}
return true;
}
void RealShillProvider::OnManagerPropertyChanged(const string& name,
const brillo::Any& value) {
if (name == shill::kDefaultServiceProperty) {
dbus::ObjectPath service_path = value.TryGet<dbus::ObjectPath>();
if (!service_path.IsValid()) {
LOG(WARNING) << "Got an invalid DefaultService path. The property value "
"contains a "
<< value.GetUndecoratedTypeName()
<< ", read as the object path: '" << service_path.value()
<< "'";
}
ProcessDefaultService(service_path);
}
}
void RealShillProvider::OnSignalConnected(const string& interface_name,
const string& signal_name,
bool successful) {
if (!successful) {
LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
<< signal_name;
}
}
bool RealShillProvider::ProcessDefaultService(
const dbus::ObjectPath& default_service_path) {
// We assume that if the service path didn't change, then the connection
// type and the tethering status of it also didn't change.
if (default_service_path_ == default_service_path)
return true;
// Update the connection status.
default_service_path_ = default_service_path;
bool is_connected = (default_service_path_.IsValid() &&
default_service_path_.value() != "/");
var_is_connected_.SetValue(is_connected);
var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
if (!is_connected) {
var_conn_type_.UnsetValue();
var_conn_tethering_.UnsetValue();
return true;
}
// We create and dispose the ServiceProxyInterface on every request.
std::unique_ptr<ServiceProxyInterface> service =
shill_proxy_->GetServiceForPath(default_service_path_);
// Get the connection properties synchronously.
brillo::VariantDictionary properties;
brillo::ErrorPtr error;
if (!service->GetProperties(&properties, &error)) {
var_conn_type_.UnsetValue();
var_conn_tethering_.UnsetValue();
return false;
}
// Get the connection tethering mode.
const auto& prop_tethering = properties.find(shill::kTetheringProperty);
if (prop_tethering == properties.end()) {
// Remove the value if not present on the service. This most likely means an
// error in shill and the policy will handle it, but we will print a log
// message as well for accessing an unused variable.
var_conn_tethering_.UnsetValue();
LOG(ERROR) << "Could not find connection type (service: "
<< default_service_path_.value() << ")";
} else {
// If the property doesn't contain a string value, the empty string will
// become kUnknown.
var_conn_tethering_.SetValue(
ParseConnectionTethering(prop_tethering->second.TryGet<string>()));
}
// Get the connection type.
const auto& prop_type = properties.find(shill::kTypeProperty);
if (prop_type == properties.end()) {
var_conn_type_.UnsetValue();
LOG(ERROR) << "Could not find connection tethering mode (service: "
<< default_service_path_.value() << ")";
} else {
string type_str = prop_type->second.TryGet<string>();
if (type_str == shill::kTypeVPN) {
const auto& prop_physical =
properties.find(shill::kPhysicalTechnologyProperty);
if (prop_physical == properties.end()) {
LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
<< " connection (service: " << default_service_path_.value()
<< "). Using default kUnknown value.";
var_conn_type_.SetValue(ConnectionType::kUnknown);
} else {
var_conn_type_.SetValue(
ParseConnectionType(prop_physical->second.TryGet<string>()));
}
} else {
var_conn_type_.SetValue(ParseConnectionType(type_str));
}
}
return true;
}
} // namespace chromeos_update_manager