real_shill_provider.cc revision 44666f97392f1f0f8be292fe6a4edcf9237540df
1// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/update_manager/real_shill_provider.h"
6
7#include <string>
8
9#include <base/logging.h>
10#include <base/strings/stringprintf.h>
11#include <chromeos/dbus/service_constants.h>
12
13#include "update_engine/glib_utils.h"
14
15using std::string;
16
17namespace {
18
19// Looks up a |key| in a GLib |hash_table| and returns the unboxed string from
20// the corresponding GValue, if found.
21const char* GetStrProperty(GHashTable* hash_table, const char* key) {
22  auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
23  return (gval ? g_value_get_string(gval) : NULL);
24}
25
26};  // namespace
27
28
29namespace chromeos_update_manager {
30
31RealShillProvider::~RealShillProvider() {
32  // Detach signal handler, free manager proxy.
33  dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
34                               G_CALLBACK(HandlePropertyChangedStatic),
35                               this);
36  dbus_->ProxyUnref(manager_proxy_);
37}
38
39ConnectionType RealShillProvider::ParseConnectionType(const char* type_str) {
40  if (!strcmp(type_str, shill::kTypeEthernet))
41    return ConnectionType::kEthernet;
42  if (!strcmp(type_str, shill::kTypeWifi))
43    return ConnectionType::kWifi;
44  if (!strcmp(type_str, shill::kTypeWimax))
45    return ConnectionType::kWimax;
46  if (!strcmp(type_str, shill::kTypeBluetooth))
47    return ConnectionType::kBluetooth;
48  if (!strcmp(type_str, shill::kTypeCellular))
49    return ConnectionType::kCellular;
50
51  return ConnectionType::kUnknown;
52}
53
54ConnectionTethering RealShillProvider::ParseConnectionTethering(
55    const char* tethering_str) {
56  if (!strcmp(tethering_str, shill::kTetheringNotDetectedState))
57    return ConnectionTethering::kNotDetected;
58  if (!strcmp(tethering_str, shill::kTetheringSuspectedState))
59    return ConnectionTethering::kSuspected;
60  if (!strcmp(tethering_str, shill::kTetheringConfirmedState))
61    return ConnectionTethering::kConfirmed;
62
63  return ConnectionTethering::kUnknown;
64}
65
66bool RealShillProvider::Init() {
67  // Obtain a DBus connection.
68  GError* error = NULL;
69  connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
70  if (!connection_) {
71    LOG(ERROR) << "Failed to initialize DBus connection: "
72               << chromeos_update_engine::utils::GetAndFreeGError(&error);
73    return false;
74  }
75
76  // Allocate a shill manager proxy.
77  manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
78                            shill::kFlimflamManagerInterface);
79
80  // Subscribe to the manager's PropertyChanged signal.
81  dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
82                          G_TYPE_STRING, G_TYPE_VALUE);
83  dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
84                            G_CALLBACK(HandlePropertyChangedStatic),
85                            this, NULL);
86
87  // Attempt to read initial connection status. Even if this fails because shill
88  // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
89  // signal as soon as it comes up, so this is not a critical step.
90  GHashTable* hash_table = NULL;
91  if (GetProperties(manager_proxy_, &hash_table)) {
92    GValue* value = reinterpret_cast<GValue*>(
93        g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
94    ProcessDefaultService(value);
95    g_hash_table_unref(hash_table);
96  }
97
98  return true;
99}
100
101DBusGProxy* RealShillProvider::GetProxy(const char* path,
102                                        const char* interface) {
103  return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
104                                path, interface);
105}
106
107bool RealShillProvider::GetProperties(DBusGProxy* proxy,
108                                      GHashTable** result_p) {
109  GError* error = NULL;
110  if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
111                            result_p)) {
112    LOG(ERROR) << "Calling shill via DBus proxy failed: "
113               << chromeos_update_engine::utils::GetAndFreeGError(&error);
114    return false;
115  }
116  return true;
117}
118
119bool RealShillProvider::ProcessDefaultService(GValue* value) {
120  // Decode the string from the boxed value.
121  const char* default_service_path_str = NULL;
122  if (!(value && (default_service_path_str = g_value_get_string(value))))
123    return false;
124
125  // Anything changed?
126  if (default_service_path_ == default_service_path_str)
127    return true;
128
129  // Update the connection status.
130  default_service_path_ = default_service_path_str;
131  bool is_connected = (default_service_path_ != "/");
132  var_is_connected_.SetValue(is_connected);
133  var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
134
135  // Update the connection attributes.
136  if (is_connected) {
137    DBusGProxy* service_proxy = GetProxy(default_service_path_.c_str(),
138                                         shill::kFlimflamServiceInterface);
139    GHashTable* hash_table = NULL;
140    if (GetProperties(service_proxy, &hash_table)) {
141      // Get the connection type.
142      const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
143      if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
144        type_str = GetStrProperty(hash_table,
145                                  shill::kPhysicalTechnologyProperty);
146      }
147      if (type_str) {
148        var_conn_type_.SetValue(ParseConnectionType(type_str));
149      } else {
150        var_conn_type_.UnsetValue();
151        LOG(ERROR) << "Could not find connection type ("
152                   << default_service_path_ << ")";
153      }
154
155      // Get the connection tethering mode.
156      const char* tethering_str = GetStrProperty(hash_table,
157                                                 shill::kTetheringProperty);
158      if (tethering_str) {
159        var_conn_tethering_.SetValue(ParseConnectionTethering(tethering_str));
160      } else {
161        var_conn_tethering_.UnsetValue();
162        LOG(ERROR) << "Could not find connection tethering mode ("
163                   << default_service_path_ << ")";
164      }
165
166      g_hash_table_unref(hash_table);
167    }
168    dbus_->ProxyUnref(service_proxy);
169  } else {
170    var_conn_type_.UnsetValue();
171    var_conn_tethering_.UnsetValue();
172  }
173
174  return true;
175}
176
177void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
178                                              const char* name, GValue* value) {
179  if (!strcmp(name, shill::kDefaultServiceProperty))
180    ProcessDefaultService(value);
181}
182
183void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
184                                                    const char* name,
185                                                    GValue* value,
186                                                    void* data) {
187  auto obj = reinterpret_cast<RealShillProvider*>(data);
188  obj->HandlePropertyChanged(proxy, name, value);
189}
190
191}  // namespace chromeos_update_manager
192