blob: f6c0217f56746c0ea3e822d0decfbf9ebd302271 [file] [log] [blame]
// Copyright (c) 2012 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/connection_manager.h"
#include <set>
#include <string>
#include <base/stl_util.h>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/dbus-glib.h>
#include <glib.h>
#include <policy/device_policy.h>
#include "update_engine/prefs.h"
#include "update_engine/system_state.h"
#include "update_engine/utils.h"
using std::set;
using std::string;
namespace chromeos_update_engine {
namespace {
// Gets the DbusGProxy for FlimFlam. Must be free'd with ProxyUnref()
bool GetFlimFlamProxy(DBusWrapperInterface* dbus_iface,
const char* path,
const char* interface,
DBusGProxy** out_proxy) {
DBusGConnection* bus;
DBusGProxy* proxy;
GError* error = nullptr;
bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
if (!bus) {
LOG(ERROR) << "Failed to get system bus";
return false;
}
proxy = dbus_iface->ProxyNewForName(bus, shill::kFlimflamServiceName, path,
interface);
*out_proxy = proxy;
return true;
}
// On success, caller owns the GHashTable at out_hash_table.
// Returns true on success.
bool GetProperties(DBusWrapperInterface* dbus_iface,
const char* path,
const char* interface,
GHashTable** out_hash_table) {
DBusGProxy* proxy;
GError* error = nullptr;
TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
path,
interface,
&proxy));
gboolean rc = dbus_iface->ProxyCall_0_1(proxy,
"GetProperties",
&error,
out_hash_table);
dbus_iface->ProxyUnref(proxy);
if (rc == FALSE) {
LOG(ERROR) << "dbus_g_proxy_call failed";
return false;
}
return true;
}
// Returns (via out_path) the default network path, or empty string if
// there's no network up.
// Returns true on success.
bool GetDefaultServicePath(DBusWrapperInterface* dbus_iface, string* out_path) {
GHashTable* hash_table = nullptr;
TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
shill::kFlimflamServicePath,
shill::kFlimflamManagerInterface,
&hash_table));
GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
"Services"));
GPtrArray* array = nullptr;
bool success = false;
if (G_VALUE_HOLDS(value, DBUS_TYPE_G_OBJECT_PATH_ARRAY) &&
(array = reinterpret_cast<GPtrArray*>(g_value_get_boxed(value))) &&
(array->len > 0)) {
*out_path = static_cast<const char*>(g_ptr_array_index(array, 0));
success = true;
}
g_hash_table_unref(hash_table);
return success;
}
NetworkConnectionType ParseConnectionType(const char* type_str) {
if (!strcmp(type_str, shill::kTypeEthernet)) {
return kNetEthernet;
} else if (!strcmp(type_str, shill::kTypeWifi)) {
return kNetWifi;
} else if (!strcmp(type_str, shill::kTypeWimax)) {
return kNetWimax;
} else if (!strcmp(type_str, shill::kTypeBluetooth)) {
return kNetBluetooth;
} else if (!strcmp(type_str, shill::kTypeCellular)) {
return kNetCellular;
}
return kNetUnknown;
}
NetworkTethering ParseTethering(const char* tethering_str) {
if (!strcmp(tethering_str, shill::kTetheringNotDetectedState)) {
return NetworkTethering::kNotDetected;
} else if (!strcmp(tethering_str, shill::kTetheringSuspectedState)) {
return NetworkTethering::kSuspected;
} else if (!strcmp(tethering_str, shill::kTetheringConfirmedState)) {
return NetworkTethering::kConfirmed;
}
LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
return NetworkTethering::kUnknown;
}
bool GetServicePathProperties(DBusWrapperInterface* dbus_iface,
const string& path,
NetworkConnectionType* out_type,
NetworkTethering* out_tethering) {
GHashTable* hash_table = nullptr;
TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
path.c_str(),
shill::kFlimflamServiceInterface,
&hash_table));
// Populate the out_tethering.
GValue* value =
reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
shill::kTetheringProperty));
const char* tethering_str = nullptr;
if (value != nullptr)
tethering_str = g_value_get_string(value);
if (tethering_str != nullptr) {
*out_tethering = ParseTethering(tethering_str);
} else {
// Set to Unknown if not present.
*out_tethering = NetworkTethering::kUnknown;
}
// Populate the out_type property.
value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
shill::kTypeProperty));
const char* type_str = nullptr;
bool success = false;
if (value != nullptr && (type_str = g_value_get_string(value)) != nullptr) {
success = true;
if (!strcmp(type_str, shill::kTypeVPN)) {
value = reinterpret_cast<GValue*>(
g_hash_table_lookup(hash_table, shill::kPhysicalTechnologyProperty));
if (value != nullptr &&
(type_str = g_value_get_string(value)) != nullptr) {
*out_type = ParseConnectionType(type_str);
} else {
LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
<< " connection (service: " << path << "). Returning default"
<< " kNetUnknown value.";
*out_type = kNetUnknown;
}
} else {
*out_type = ParseConnectionType(type_str);
}
}
g_hash_table_unref(hash_table);
return success;
}
} // namespace
ConnectionManager::ConnectionManager(SystemState *system_state)
: system_state_(system_state) {}
bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
NetworkTethering tethering) const {
switch (type) {
case kNetBluetooth:
return false;
case kNetCellular: {
set<string> allowed_types;
const policy::DevicePolicy* device_policy =
system_state_->device_policy();
// A device_policy is loaded in a lazy way right before an update check,
// so the device_policy should be already loaded at this point. If it's
// not, return a safe value for this setting.
if (!device_policy) {
LOG(INFO) << "Disabling updates over cellular networks as there's no "
"device policy loaded yet.";
return false;
}
if (device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
// The update setting is enforced by the device policy.
if (!ContainsKey(allowed_types, shill::kTypeCellular)) {
LOG(INFO) << "Disabling updates over cellular connection as it's not "
"allowed in the device policy.";
return false;
}
LOG(INFO) << "Allowing updates over cellular per device policy.";
return true;
} else {
// There's no update setting in the device policy, using the local user
// setting.
PrefsInterface* prefs = system_state_->prefs();
if (!prefs || !prefs->Exists(kPrefsUpdateOverCellularPermission)) {
LOG(INFO) << "Disabling updates over cellular connection as there's "
"no device policy setting nor user preference present.";
return false;
}
bool stored_value;
if (!prefs->GetBoolean(kPrefsUpdateOverCellularPermission,
&stored_value)) {
return false;
}
if (!stored_value) {
LOG(INFO) << "Disabling updates over cellular connection per user "
"setting.";
return false;
}
LOG(INFO) << "Allowing updates over cellular per user setting.";
return true;
}
}
default:
if (tethering == NetworkTethering::kConfirmed) {
// Treat this connection as if it is a cellular connection.
LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
"setting.";
return IsUpdateAllowedOver(kNetCellular, NetworkTethering::kUnknown);
}
return true;
}
}
const char* ConnectionManager::StringForConnectionType(
NetworkConnectionType type) const {
static const char* const kValues[] = {shill::kTypeEthernet,
shill::kTypeWifi,
shill::kTypeWimax,
shill::kTypeBluetooth,
shill::kTypeCellular};
if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
return "Unknown";
}
return kValues[type];
}
const char* ConnectionManager::StringForTethering(
NetworkTethering tethering) const {
switch (tethering) {
case NetworkTethering::kNotDetected:
return shill::kTetheringNotDetectedState;
case NetworkTethering::kSuspected:
return shill::kTetheringSuspectedState;
case NetworkTethering::kConfirmed:
return shill::kTetheringConfirmedState;
case NetworkTethering::kUnknown:
return "Unknown";
}
// The program shouldn't reach this point, but the compiler isn't smart
// enough to infer that.
return "Unknown";
}
bool ConnectionManager::GetConnectionProperties(
DBusWrapperInterface* dbus_iface,
NetworkConnectionType* out_type,
NetworkTethering* out_tethering) const {
string default_service_path;
TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
&default_service_path));
TEST_AND_RETURN_FALSE(GetServicePathProperties(dbus_iface,
default_service_path,
out_type, out_tethering));
return true;
}
} // namespace chromeos_update_engine