connection_manager.cc revision 88b591f24cb3f94f982d7024c2e8ed25c2cc26a2
1// Copyright (c) 2012 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/connection_manager.h"
6
7#include <set>
8#include <string>
9
10#include <base/stl_util.h>
11#include <base/strings/string_util.h>
12#include <chromeos/dbus/service_constants.h>
13#include <dbus/dbus-glib.h>
14#include <glib.h>
15#include <policy/device_policy.h>
16
17#include "update_engine/prefs.h"
18#include "update_engine/system_state.h"
19#include "update_engine/utils.h"
20
21using std::set;
22using std::string;
23
24namespace chromeos_update_engine {
25
26namespace {
27
28// Gets the DbusGProxy for FlimFlam. Must be free'd with ProxyUnref()
29bool GetFlimFlamProxy(DBusWrapperInterface* dbus_iface,
30                      const char* path,
31                      const char* interface,
32                      DBusGProxy** out_proxy) {
33  DBusGConnection* bus;
34  DBusGProxy* proxy;
35  GError* error = nullptr;
36
37  bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
38  if (!bus) {
39    LOG(ERROR) << "Failed to get system bus";
40    return false;
41  }
42  proxy = dbus_iface->ProxyNewForName(bus, shill::kFlimflamServiceName, path,
43                                      interface);
44  *out_proxy = proxy;
45  return true;
46}
47
48// On success, caller owns the GHashTable at out_hash_table.
49// Returns true on success.
50bool GetProperties(DBusWrapperInterface* dbus_iface,
51                   const char* path,
52                   const char* interface,
53                   GHashTable** out_hash_table) {
54  DBusGProxy* proxy;
55  GError* error = nullptr;
56
57  TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
58                                         path,
59                                         interface,
60                                         &proxy));
61
62  gboolean rc = dbus_iface->ProxyCall_0_1(proxy,
63                                          "GetProperties",
64                                          &error,
65                                          out_hash_table);
66  dbus_iface->ProxyUnref(proxy);
67  if (rc == FALSE) {
68    LOG(ERROR) << "dbus_g_proxy_call failed";
69    return false;
70  }
71
72  return true;
73}
74
75// Returns (via out_path) the default network path, or empty string if
76// there's no network up.
77// Returns true on success.
78bool GetDefaultServicePath(DBusWrapperInterface* dbus_iface, string* out_path) {
79  GHashTable* hash_table = nullptr;
80
81  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
82                                      shill::kFlimflamServicePath,
83                                      shill::kFlimflamManagerInterface,
84                                      &hash_table));
85
86  GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
87                                                                "Services"));
88  GPtrArray* array = nullptr;
89  bool success = false;
90  if (G_VALUE_HOLDS(value, DBUS_TYPE_G_OBJECT_PATH_ARRAY) &&
91      (array = reinterpret_cast<GPtrArray*>(g_value_get_boxed(value))) &&
92      (array->len > 0)) {
93    *out_path = static_cast<const char*>(g_ptr_array_index(array, 0));
94    success = true;
95  }
96
97  g_hash_table_unref(hash_table);
98  return success;
99}
100
101NetworkConnectionType ParseConnectionType(const char* type_str) {
102  if (!strcmp(type_str, shill::kTypeEthernet)) {
103    return kNetEthernet;
104  } else if (!strcmp(type_str, shill::kTypeWifi)) {
105    return kNetWifi;
106  } else if (!strcmp(type_str, shill::kTypeWimax)) {
107    return kNetWimax;
108  } else if (!strcmp(type_str, shill::kTypeBluetooth)) {
109    return kNetBluetooth;
110  } else if (!strcmp(type_str, shill::kTypeCellular)) {
111    return kNetCellular;
112  }
113  return kNetUnknown;
114}
115
116NetworkTethering ParseTethering(const char* tethering_str) {
117  if (!strcmp(tethering_str, shill::kTetheringNotDetectedState)) {
118    return NetworkTethering::kNotDetected;
119  } else if (!strcmp(tethering_str, shill::kTetheringSuspectedState)) {
120    return NetworkTethering::kSuspected;
121  } else if (!strcmp(tethering_str, shill::kTetheringConfirmedState)) {
122    return NetworkTethering::kConfirmed;
123  }
124  LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
125  return NetworkTethering::kUnknown;
126}
127
128bool GetServicePathProperties(DBusWrapperInterface* dbus_iface,
129                              const string& path,
130                              NetworkConnectionType* out_type,
131                              NetworkTethering* out_tethering) {
132  GHashTable* hash_table = nullptr;
133
134  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
135                                      path.c_str(),
136                                      shill::kFlimflamServiceInterface,
137                                      &hash_table));
138
139  // Populate the out_tethering.
140  GValue* value =
141      reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
142                                                    shill::kTetheringProperty));
143  const char* tethering_str = nullptr;
144
145  if (value != nullptr)
146    tethering_str = g_value_get_string(value);
147  if (tethering_str != nullptr) {
148    *out_tethering = ParseTethering(tethering_str);
149  } else {
150    // Set to Unknown if not present.
151    *out_tethering = NetworkTethering::kUnknown;
152  }
153
154  // Populate the out_type property.
155  value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
156                                                        shill::kTypeProperty));
157  const char* type_str = nullptr;
158  bool success = false;
159  if (value != nullptr && (type_str = g_value_get_string(value)) != nullptr) {
160    success = true;
161    if (!strcmp(type_str, shill::kTypeVPN)) {
162      value = reinterpret_cast<GValue*>(
163          g_hash_table_lookup(hash_table, shill::kPhysicalTechnologyProperty));
164      if (value != nullptr &&
165          (type_str = g_value_get_string(value)) != nullptr) {
166        *out_type = ParseConnectionType(type_str);
167      } else {
168        LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
169                   << " connection (service: " << path << "). Returning default"
170                   << " kNetUnknown value.";
171        *out_type = kNetUnknown;
172      }
173    } else {
174      *out_type = ParseConnectionType(type_str);
175    }
176  }
177  g_hash_table_unref(hash_table);
178  return success;
179}
180
181}  // namespace
182
183ConnectionManager::ConnectionManager(SystemState *system_state)
184    :  system_state_(system_state) {}
185
186bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
187                                            NetworkTethering tethering) const {
188  switch (type) {
189    case kNetBluetooth:
190      return false;
191
192    case kNetCellular: {
193      set<string> allowed_types;
194      const policy::DevicePolicy* device_policy =
195          system_state_->device_policy();
196
197      // A device_policy is loaded in a lazy way right before an update check,
198      // so the device_policy should be already loaded at this point. If it's
199      // not, return a safe value for this setting.
200      if (!device_policy) {
201        LOG(INFO) << "Disabling updates over cellular networks as there's no "
202                     "device policy loaded yet.";
203        return false;
204      }
205
206      if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
207        // The update setting is enforced by the device policy.
208
209        if (!ContainsKey(allowed_types, shill::kTypeCellular)) {
210          LOG(INFO) << "Disabling updates over cellular connection as it's not "
211                       "allowed in the device policy.";
212          return false;
213        }
214
215        LOG(INFO) << "Allowing updates over cellular per device policy.";
216        return true;
217      } else {
218        // There's no update setting in the device policy, using the local user
219        // setting.
220        PrefsInterface* prefs = system_state_->prefs();
221
222        if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
223          LOG(INFO) << "Disabling updates over cellular connection as there's "
224                       "no device policy setting nor user preference present.";
225          return false;
226        }
227
228        bool stored_value;
229        if (!prefs->GetBoolean(kPrefsUpdateOverCellularPermission,
230                               &stored_value)) {
231          return false;
232        }
233
234        if (!stored_value) {
235          LOG(INFO) << "Disabling updates over cellular connection per user "
236                       "setting.";
237          return false;
238        }
239        LOG(INFO) << "Allowing updates over cellular per user setting.";
240        return true;
241      }
242    }
243
244    default:
245      if (tethering == NetworkTethering::kConfirmed) {
246        // Treat this connection as if it is a cellular connection.
247        LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
248                     "setting.";
249        return IsUpdateAllowedOver(kNetCellular, NetworkTethering::kUnknown);
250      }
251      return true;
252  }
253}
254
255const char* ConnectionManager::StringForConnectionType(
256    NetworkConnectionType type) const {
257  static const char* const kValues[] = {shill::kTypeEthernet,
258                                        shill::kTypeWifi,
259                                        shill::kTypeWimax,
260                                        shill::kTypeBluetooth,
261                                        shill::kTypeCellular};
262  if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
263    return "Unknown";
264  }
265  return kValues[type];
266}
267
268const char* ConnectionManager::StringForTethering(
269    NetworkTethering tethering) const {
270  switch (tethering) {
271    case NetworkTethering::kNotDetected:
272      return shill::kTetheringNotDetectedState;
273    case NetworkTethering::kSuspected:
274      return shill::kTetheringSuspectedState;
275    case NetworkTethering::kConfirmed:
276      return shill::kTetheringConfirmedState;
277    case NetworkTethering::kUnknown:
278      return "Unknown";
279  }
280  // The program shouldn't reach this point, but the compiler isn't smart
281  // enough to infer that.
282  return "Unknown";
283}
284
285bool ConnectionManager::GetConnectionProperties(
286    DBusWrapperInterface* dbus_iface,
287    NetworkConnectionType* out_type,
288    NetworkTethering* out_tethering) const {
289  string default_service_path;
290  TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
291                                              &default_service_path));
292  TEST_AND_RETURN_FALSE(GetServicePathProperties(dbus_iface,
293                                                 default_service_path,
294                                                 out_type, out_tethering));
295  return true;
296}
297
298}  // namespace chromeos_update_engine
299