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