| // 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 |