blob: 609fd0191c70bb9e86508dadea0fa794f230b00d [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/update_manager/real_shill_provider.h"
#include <string>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include "update_engine/glib_utils.h"
using std::string;
namespace {
// Looks up a |key| in a GLib |hash_table| and returns the unboxed string from
// the corresponding GValue, if found.
const char* GetStrProperty(GHashTable* hash_table, const char* key) {
auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
return (gval ? g_value_get_string(gval) : nullptr);
}
}; // namespace
namespace chromeos_update_manager {
RealShillProvider::~RealShillProvider() {
// Detach signal handler, free manager proxy.
dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
G_CALLBACK(HandlePropertyChangedStatic),
this);
dbus_->ProxyUnref(manager_proxy_);
}
ConnectionType RealShillProvider::ParseConnectionType(const char* type_str) {
if (!strcmp(type_str, shill::kTypeEthernet))
return ConnectionType::kEthernet;
if (!strcmp(type_str, shill::kTypeWifi))
return ConnectionType::kWifi;
if (!strcmp(type_str, shill::kTypeWimax))
return ConnectionType::kWimax;
if (!strcmp(type_str, shill::kTypeBluetooth))
return ConnectionType::kBluetooth;
if (!strcmp(type_str, shill::kTypeCellular))
return ConnectionType::kCellular;
return ConnectionType::kUnknown;
}
ConnectionTethering RealShillProvider::ParseConnectionTethering(
const char* tethering_str) {
if (!strcmp(tethering_str, shill::kTetheringNotDetectedState))
return ConnectionTethering::kNotDetected;
if (!strcmp(tethering_str, shill::kTetheringSuspectedState))
return ConnectionTethering::kSuspected;
if (!strcmp(tethering_str, shill::kTetheringConfirmedState))
return ConnectionTethering::kConfirmed;
return ConnectionTethering::kUnknown;
}
bool RealShillProvider::Init() {
// Obtain a DBus connection.
GError* error = nullptr;
connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
if (!connection_) {
LOG(ERROR) << "Failed to initialize DBus connection: "
<< chromeos_update_engine::utils::GetAndFreeGError(&error);
return false;
}
// Allocate a shill manager proxy.
manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
shill::kFlimflamManagerInterface);
// Subscribe to the manager's PropertyChanged signal.
dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
G_TYPE_STRING, G_TYPE_VALUE);
dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
G_CALLBACK(HandlePropertyChangedStatic),
this, nullptr);
// 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.
GHashTable* hash_table = nullptr;
if (GetProperties(manager_proxy_, &hash_table)) {
GValue* value = reinterpret_cast<GValue*>(
g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
ProcessDefaultService(value);
g_hash_table_unref(hash_table);
}
return true;
}
DBusGProxy* RealShillProvider::GetProxy(const char* path,
const char* interface) {
return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
path, interface);
}
bool RealShillProvider::GetProperties(DBusGProxy* proxy,
GHashTable** result_p) {
GError* error = nullptr;
if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
result_p)) {
LOG(ERROR) << "Calling shill via DBus proxy failed: "
<< chromeos_update_engine::utils::GetAndFreeGError(&error);
return false;
}
return true;
}
bool RealShillProvider::ProcessDefaultService(GValue* value) {
// Decode the string from the boxed value.
const char* default_service_path_str = nullptr;
if (!(value && (default_service_path_str = g_value_get_string(value))))
return false;
// Anything changed?
if (default_service_path_ == default_service_path_str)
return true;
// Update the connection status.
default_service_path_ = default_service_path_str;
bool is_connected = (default_service_path_ != "/");
var_is_connected_.SetValue(is_connected);
var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
// Update the connection attributes.
if (is_connected) {
DBusGProxy* service_proxy = GetProxy(default_service_path_.c_str(),
shill::kFlimflamServiceInterface);
GHashTable* hash_table = nullptr;
if (GetProperties(service_proxy, &hash_table)) {
// Get the connection type.
const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
type_str = GetStrProperty(hash_table,
shill::kPhysicalTechnologyProperty);
}
if (type_str) {
var_conn_type_.SetValue(ParseConnectionType(type_str));
} else {
var_conn_type_.UnsetValue();
LOG(ERROR) << "Could not find connection type ("
<< default_service_path_ << ")";
}
// Get the connection tethering mode.
const char* tethering_str = GetStrProperty(hash_table,
shill::kTetheringProperty);
if (tethering_str) {
var_conn_tethering_.SetValue(ParseConnectionTethering(tethering_str));
} else {
var_conn_tethering_.UnsetValue();
LOG(ERROR) << "Could not find connection tethering mode ("
<< default_service_path_ << ")";
}
g_hash_table_unref(hash_table);
}
dbus_->ProxyUnref(service_proxy);
} else {
var_conn_type_.UnsetValue();
var_conn_tethering_.UnsetValue();
}
return true;
}
void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
const char* name, GValue* value) {
if (!strcmp(name, shill::kDefaultServiceProperty))
ProcessDefaultService(value);
}
void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
const char* name,
GValue* value,
void* data) {
auto obj = reinterpret_cast<RealShillProvider*>(data);
obj->HandlePropertyChanged(proxy, name, value);
}
} // namespace chromeos_update_manager