UpdateEngine-side changes to allow updates over 3G based on device policy.

Some enterprise chromebooks have only 3G and hence they need the ability
to update over 3G if the enterprise policy allows that. This CL adds
the support in update_engine to enable that.

BUG=chromium-os:31099
TEST=Tested E2E on 3G, added unit tests and did regression testing.
CQ-DEPEND=I1a55a392f3dc0f12d917eb45dcf0456b57735514
Change-Id: I121bda35e54fa6c35e002a76db198d13b72b650e
Reviewed-on: https://gerrit.chromium.org/gerrit/25470
Commit-Ready: Jay Srinivasan <jaysri@chromium.org>
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
diff --git a/connection_manager.cc b/connection_manager.cc
new file mode 100644
index 0000000..438ed41
--- /dev/null
+++ b/connection_manager.cc
@@ -0,0 +1,213 @@
+// 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 <string>
+
+#include <base/stl_util.h>
+#include <base/string_util.h>
+#include <chromeos/dbus/service_constants.h>
+#include <dbus/dbus-glib.h>
+#include <glib.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(DbusGlibInterface* dbus_iface,
+                      const char* path,
+                      const char* interface,
+                      DBusGProxy** out_proxy) {
+  DBusGConnection* bus;
+  DBusGProxy* proxy;
+  GError* error = NULL;
+
+  bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
+  if (!bus) {
+    LOG(ERROR) << "Failed to get system bus";
+    return false;
+  }
+  proxy = dbus_iface->ProxyNewForNameOwner(bus,
+                                           flimflam::kFlimflamServiceName,
+                                           path,
+                                           interface,
+                                           &error);
+  if (!proxy) {
+    LOG(ERROR) << "Error getting FlimFlam proxy: "
+               << utils::GetAndFreeGError(&error);
+    return false;
+  }
+  *out_proxy = proxy;
+  return true;
+}
+
+// On success, caller owns the GHashTable at out_hash_table.
+// Returns true on success.
+bool GetProperties(DbusGlibInterface* dbus_iface,
+                   const char* path,
+                   const char* interface,
+                   GHashTable** out_hash_table) {
+  DBusGProxy* proxy;
+  GError* error = NULL;
+
+  TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
+                                         path,
+                                         interface,
+                                         &proxy));
+
+  gboolean rc = dbus_iface->ProxyCall(proxy,
+                                      "GetProperties",
+                                      &error,
+                                      G_TYPE_INVALID,
+                                      dbus_g_type_get_map("GHashTable",
+                                                          G_TYPE_STRING,
+                                                          G_TYPE_VALUE),
+                                      out_hash_table,
+                                      G_TYPE_INVALID);
+  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(DbusGlibInterface* dbus_iface, string* out_path) {
+  GHashTable* hash_table = NULL;
+
+  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
+                                      flimflam::kFlimflamServicePath,
+                                      flimflam::kFlimflamManagerInterface,
+                                      &hash_table));
+
+  GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
+                                                                "Services"));
+  GArray* array = NULL;
+  bool success = false;
+  if (G_VALUE_HOLDS(value, DBUS_TYPE_G_OBJECT_PATH_ARRAY) &&
+      (array = reinterpret_cast<GArray*>(g_value_get_boxed(value))) &&
+      (array->len > 0)) {
+    *out_path = g_array_index(array, const char*, 0);
+    success = true;
+  }
+
+  g_hash_table_unref(hash_table);
+  return success;
+}
+
+NetworkConnectionType ParseConnectionType(const char* type_str) {
+  if (!strcmp(type_str, flimflam::kTypeEthernet)) {
+    return kNetEthernet;
+  } else if (!strcmp(type_str, flimflam::kTypeWifi)) {
+    return kNetWifi;
+  } else if (!strcmp(type_str, flimflam::kTypeWimax)) {
+    return kNetWimax;
+  } else if (!strcmp(type_str, flimflam::kTypeBluetooth)) {
+    return kNetBluetooth;
+  } else if (!strcmp(type_str, flimflam::kTypeCellular)) {
+    return kNetCellular;
+  }
+  return kNetUnknown;
+}
+
+bool GetServicePathType(DbusGlibInterface* dbus_iface,
+                        const string& path,
+                        NetworkConnectionType* out_type) {
+  GHashTable* hash_table = NULL;
+
+  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
+                                      path.c_str(),
+                                      flimflam::kFlimflamServiceInterface,
+                                      &hash_table));
+
+  GValue* value = (GValue*)g_hash_table_lookup(hash_table, "Type");
+  const char* type_str = NULL;
+  bool success = false;
+  if (value != NULL && (type_str = g_value_get_string(value)) != NULL) {
+    *out_type = ParseConnectionType(type_str);
+    success = true;
+  }
+  g_hash_table_unref(hash_table);
+  return success;
+}
+
+}  // namespace {}
+
+ConnectionManager::ConnectionManager(SystemState *system_state)
+    :  system_state_(system_state) {}
+
+bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type) const {
+  switch (type) {
+    case kNetBluetooth:
+      return false;
+
+    case kNetCellular: {
+      set<string> allowed_types;
+      const policy::DevicePolicy* device_policy =
+          system_state_->GetDevicePolicy();
+      if (!device_policy) {
+        LOG(INFO) << "Disabling updates over cellular connection as there's no "
+                     "device policy object present";
+        return false;
+      }
+
+      if (!device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
+        LOG(INFO) << "Disabling updates over cellular connection as there's no "
+                     "allowed connection types from policy";
+        return false;
+      }
+
+      if (!ContainsKey(allowed_types, flimflam::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;
+    }
+
+    default:
+      return true;
+  }
+}
+
+const char* ConnectionManager::StringForConnectionType(
+    NetworkConnectionType type) const {
+  static const char* const kValues[] = {flimflam::kTypeEthernet,
+                                        flimflam::kTypeWifi,
+                                        flimflam::kTypeWimax,
+                                        flimflam::kTypeBluetooth,
+                                        flimflam::kTypeCellular};
+  if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
+    return "Unknown";
+  }
+  return kValues[type];
+}
+
+bool ConnectionManager::GetConnectionType(
+    DbusGlibInterface* dbus_iface,
+    NetworkConnectionType* out_type) const {
+  string default_service_path;
+  TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
+                                              &default_service_path));
+  TEST_AND_RETURN_FALSE(GetServicePathType(dbus_iface,
+                                           default_service_path,
+                                           out_type));
+  return true;
+}
+
+}  // namespace chromeos_update_engine