PM: Add a DBus backend to the shill provider.

This change integrates the necessary logic for the shill provider to
actually talk to shill over DBus.

* RealShillProviders now takes a DbusInterface object. PolicyManager and
  RealState were extended to propagate this object, accordingly.

* New provider_utils and general glib_utils modules.

* Minor touch-ups: removal of redundant includes and unwarranted 'using'
  clauses.

BUG=chromium:338585
TEST=Unit tests.

Change-Id: I4082b845557eff2a02a928c60c348dde0e784a2c
Reviewed-on: https://chromium-review.googlesource.com/189045
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Commit-Queue: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/policy_manager/fake_shill_provider.h b/policy_manager/fake_shill_provider.h
index f75ef2c..b05a147 100644
--- a/policy_manager/fake_shill_provider.h
+++ b/policy_manager/fake_shill_provider.h
@@ -18,11 +18,11 @@
  protected:
   virtual bool DoInit() {
     set_var_is_connected(
-        new FakeVariable<bool>("is_connected", kVariableModeAsync));
+        new FakeVariable<bool>("is_connected", kVariableModePoll));
     set_var_conn_type(
-        new FakeVariable<ShillConnType>("conn_type", kVariableModeAsync));
+        new FakeVariable<ShillConnType>("conn_type", kVariableModePoll));
     set_var_conn_last_changed(
-        new FakeVariable<Time>("conn_last_changed", kVariableModeAsync));
+        new FakeVariable<base::Time>("conn_last_changed", kVariableModePoll));
     return true;
   }
 
diff --git a/policy_manager/policy_manager.cc b/policy_manager/policy_manager.cc
index 146c177..027a4fe 100644
--- a/policy_manager/policy_manager.cc
+++ b/policy_manager/policy_manager.cc
@@ -17,12 +17,13 @@
    return handle_ptr->get() && (*handle_ptr)->Init();
 }
 
-bool PolicyManager::Init() {
+bool PolicyManager::Init(chromeos_update_engine::DBusWrapperInterface* dbus,
+                         chromeos_update_engine::ClockInterface* clock) {
   // TODO(deymo): Make it possible to replace this policy with a different
   // implementation with a build-time flag.
   policy_.reset(new ChromeOSPolicy());
 
-  state_.reset(new RealState());
+  state_.reset(new RealState(dbus, clock));
 
   return state_->Init();
 }
diff --git a/policy_manager/policy_manager.h b/policy_manager/policy_manager.h
index 3966ec5..291ff32 100644
--- a/policy_manager/policy_manager.h
+++ b/policy_manager/policy_manager.h
@@ -11,6 +11,8 @@
 #include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
 
+#include "update_engine/clock_interface.h"
+#include "update_engine/dbus_wrapper_interface.h"
 #include "update_engine/policy_manager/default_policy.h"
 #include "update_engine/policy_manager/policy.h"
 #include "update_engine/policy_manager/state.h"
@@ -24,7 +26,8 @@
 
   // Initializes the PolicyManager instance. Returns whether the initialization
   // succeeded.
-  bool Init();
+  bool Init(chromeos_update_engine::DBusWrapperInterface* dbus,
+            chromeos_update_engine::ClockInterface* clock);
 
   // PolicyRequest() evaluates the given policy with the provided arguments and
   // returns the result. The |policy_method| is the pointer-to-method of the
diff --git a/policy_manager/policy_manager_unittest.cc b/policy_manager/policy_manager_unittest.cc
index 9e28ebc..fd43045 100644
--- a/policy_manager/policy_manager_unittest.cc
+++ b/policy_manager/policy_manager_unittest.cc
@@ -8,10 +8,11 @@
 
 #include <base/bind.h>
 #include <base/memory/scoped_ptr.h>
-#include <base/time.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 
+#include "update_engine/fake_clock.h"
+#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/policy_manager/default_policy.h"
 #include "update_engine/policy_manager/mock_policy.h"
 #include "update_engine/policy_manager/pmtest_utils.h"
@@ -20,24 +21,34 @@
 
 using base::Bind;
 using base::Callback;
-using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::MockDBusWrapper;
 using std::pair;
 using std::string;
 using std::vector;
-
-using testing::_;
+using testing::NiceMock;
 using testing::Return;
 using testing::StrictMock;
+using testing::_;
+
+namespace {
+
+DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
+
+}  // namespace
 
 namespace chromeos_policy_manager {
 
 class PmPolicyManagerTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    EXPECT_TRUE(pmut_.Init());
+    EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(Return(kFakeConnection));
+    EXPECT_TRUE(pmut_.Init(&mock_dbus_, &fake_clock_));
   }
 
-  PolicyManager pmut_; // PolicyManager undert test.
+  NiceMock<MockDBusWrapper> mock_dbus_;
+  FakeClock fake_clock_;
+  PolicyManager pmut_;
 };
 
 // The FailingPolicy implements a single method and make it always fail. This
diff --git a/policy_manager/real_shill_provider.cc b/policy_manager/real_shill_provider.cc
index 279266d..60a953f 100644
--- a/policy_manager/real_shill_provider.cc
+++ b/policy_manager/real_shill_provider.cc
@@ -2,25 +2,206 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "update_engine/policy_manager/generic_variables.h"
 #include "update_engine/policy_manager/real_shill_provider.h"
 
+#include <string>
+
+#include <base/logging.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "update_engine/policy_manager/generic_variables.h"
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace {
+
+// Looks up a key in a hash table and returns the string inside of the returned
+// GValue.
+const char* GetStrProperty(GHashTable* hash_table, const char* key) {
+  auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
+  return (gval ? g_value_get_string(gval) : NULL);
+}
+
+};  // namespace
+
+
 namespace chromeos_policy_manager {
 
-// ShillProvider implementation.
+const ShillConnector::ConnStrToType ShillConnector::shill_conn_str_to_type[] = {
+  {shill::kTypeEthernet, kShillConnTypeEthernet},
+  {shill::kTypeWifi, kShillConnTypeWifi},
+  {shill::kTypeWimax, kShillConnTypeWimax},
+  {shill::kTypeBluetooth, kShillConnTypeBluetooth},
+  {shill::kTypeCellular, kShillConnTypeCellular},
+};
 
-bool RealShillProvider::DoInit(void) {
-  // TODO(garnold) Initialize with actual (or fake) DBus connection.
+bool ShillConnector::Init() {
+  GError* error = NULL;
+  connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
+  if (!connection_) {
+    LOG(ERROR) << "Failed to initialize DBus connection: "
+               << chromeos_update_engine::utils::GetAndFreeGError(&error);
+    return false;
+  }
+  manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
+                            shill::kFlimflamManagerInterface);
+  return true;
+}
 
+bool ShillConnector::GetDefaultConnection(bool* is_connected_p,
+                                          string* default_service_path_p) {
+  GHashTable* hash_table = NULL;
+  if (!GetProperties(manager_proxy_, &hash_table))
+    return false;
+  GValue* value = reinterpret_cast<GValue*>(
+      g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
+  const char* default_service_path_str = NULL;
+  bool success = false;
+  if (value && (default_service_path_str = g_value_get_string(value))) {
+    success = true;
+    *is_connected_p = strcmp(default_service_path_str, "/");
+    if (*is_connected_p)
+      *default_service_path_p = default_service_path_str;
+  }
+  g_hash_table_unref(hash_table);
+
+  return success;
+}
+
+bool ShillConnector::GetConnectionType(const string& service_path,
+                                       ShillConnType* conn_type_p) {
+  // Obtain a proxy for the service path.
+  DBusGProxy* service_proxy = GetProxy(service_path.c_str(),
+                                       shill::kFlimflamServiceInterface);
+
+  GHashTable* hash_table = NULL;
+  bool success = false;
+  bool is_vpn = false;
+  if (GetProperties(service_proxy, &hash_table)) {
+    const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
+    if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
+      is_vpn = true;
+      type_str = GetStrProperty(hash_table, shill::kPhysicalTechnologyProperty);
+    }
+    if (type_str) {
+      success = true;
+      *conn_type_p = ParseConnType(type_str);
+    }
+    g_hash_table_unref(hash_table);
+  }
+
+  if (!success) {
+    LOG(ERROR) << "Could not find type of "
+               << (is_vpn ? "physical connection underlying VPN " : "")
+               << "connection (" << service_path << ")";
+  }
+
+  dbus_->ProxyUnref(service_proxy);
+  return success;
+}
+
+DBusGProxy* ShillConnector::GetProxy(const char* path, const char* interface) {
+  return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
+                                path, interface);
+}
+
+ShillConnType ShillConnector::ParseConnType(const char* str) {
+  for (unsigned i = 0; i < arraysize(shill_conn_str_to_type); i++)
+    if (!strcmp(str, shill_conn_str_to_type[i].str))
+      return shill_conn_str_to_type[i].type;
+
+  return kShillConnTypeUnknown;
+}
+
+// Issues a GetProperties call through a given |proxy|, storing the result to
+// |*result_p|. Returns |true| on success.
+bool ShillConnector::GetProperties(DBusGProxy* proxy, GHashTable** result_p) {
+  GError* error = NULL;
+  if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
+                            result_p)) {
+    LOG(ERROR) << "Calling shill via DBus proxy failed: "
+               << chromeos_update_engine::utils::GetAndFreeGError(&error);
+    return false;
+  }
+  return true;
+}
+
+// A variable returning whether or not we have a network connection.
+class IsConnectedVariable : public Variable<bool> {
+ public:
+  IsConnectedVariable(const string& name, ShillConnector* connector,
+                      LastValueTracker<bool>* is_connected_tracker)
+      : Variable<bool>(name, kVariableModePoll),
+        connector_(connector),
+        is_connected_tracker_(is_connected_tracker) {}
+
+ protected:
+  virtual const bool* GetValue(base::TimeDelta timeout, string* errmsg) {
+    bool is_connected;
+    string default_service_path;
+    if (!connector_->GetDefaultConnection(&is_connected, &default_service_path))
+      return NULL;;
+
+    return new bool(is_connected_tracker_->Update(is_connected));
+  }
+
+ private:
+  ShillConnector* connector_;
+  LastValueTracker<bool>* is_connected_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(IsConnectedVariable);
+};
+
+// A variable returning the curent connection type.
+class ConnTypeVariable : public Variable<ShillConnType> {
+ public:
+  ConnTypeVariable(const string& name, ShillConnector* connector,
+                   LastValueTracker<bool>* is_connected_tracker)
+      : Variable<ShillConnType>(name, kVariableModePoll),
+        connector_(connector),
+        is_connected_tracker_(is_connected_tracker) {}
+
+ protected:
+  virtual const ShillConnType* GetValue(base::TimeDelta timeout,
+                                        string* errmsg) {
+    bool is_connected;
+    string default_service_path;
+    ShillConnType conn_type;
+    if (!(connector_->GetDefaultConnection(&is_connected,
+                                           &default_service_path) &&
+          is_connected &&
+          connector_->GetConnectionType(default_service_path, &conn_type)))
+      return NULL;
+
+    is_connected_tracker_->Update(is_connected);
+    return new ShillConnType(conn_type);
+  }
+
+ private:
+  ShillConnector* connector_;
+  LastValueTracker<bool>* is_connected_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnTypeVariable);
+};
+
+// A real implementation of the ShillProvider.
+bool RealShillProvider::DoInit() {
+  // Initialize a DBus connection and obtain the shill manager proxy.
+  connector_.reset(new ShillConnector(dbus_));
+  if (!connector_->Init())
+    return false;
+
+  // Initialize variables.
   set_var_is_connected(
-      new CopyVariable<bool>("is_connected", kVariableModeAsync,
-                             is_connected_));
+      new IsConnectedVariable("is_connected", connector_.get(),
+                              &is_connected_tracker_));
   set_var_conn_type(
-      new CopyVariable<ShillConnType>("conn_type", kVariableModeAsync,
-                                      conn_type_));
+      new ConnTypeVariable("conn_type", connector_.get(),
+                           &is_connected_tracker_));
   set_var_conn_last_changed(
-      new CopyVariable<Time>("conn_last_changed", kVariableModeAsync,
-                             conn_last_changed_));
+      new CopyVariable<base::Time>("conn_last_changed", kVariableModePoll,
+                                   conn_last_changed_));
   return true;
 }
 
diff --git a/policy_manager/real_shill_provider.h b/policy_manager/real_shill_provider.h
index 1a120b7..26de44f 100644
--- a/policy_manager/real_shill_provider.h
+++ b/policy_manager/real_shill_provider.h
@@ -5,36 +5,124 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_REAL_SHILL_PROVIDER_H
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_REAL_SHILL_PROVIDER_H
 
+// TODO(garnold) Much of the functionality in this module was adapted from the
+// update engine's connection_manager.  We need to make sure to deprecate use of
+// connection manager when the time comes.
+
+#include <string>
+
+#include <base/time.h>
+
+#include "update_engine/clock_interface.h"
+#include "update_engine/dbus_wrapper_interface.h"
 #include "update_engine/policy_manager/shill_provider.h"
 
-using base::Time;
+using chromeos_update_engine::ClockInterface;
+using chromeos_update_engine::DBusWrapperInterface;
 
 namespace chromeos_policy_manager {
 
+// A tracker for the last reported value. Whenever Update() is called with a new
+// value, the current time is written to a pointed time object.
+template<typename T>
+class LastValueTracker {
+ public:
+  LastValueTracker(ClockInterface* clock, T init_val, base::Time* time_p)
+      : clock_(clock), last_val_(init_val), time_p_(time_p) {}
+
+  const T& Update(const T& curr_val) {
+    if (curr_val != last_val_) {
+      last_val_ = curr_val;
+      *time_p_ = clock_->GetWallclockTime();
+    }
+    return curr_val;
+  }
+
+ private:
+  ClockInterface* const clock_;
+  T last_val_;
+  base::Time* time_p_;
+};
+
+// A DBus connector for making shill queries.
+class ShillConnector {
+ public:
+  ShillConnector(DBusWrapperInterface* dbus) : dbus_(dbus) {}
+
+  ~ShillConnector() {
+    if (manager_proxy_)
+      dbus_->ProxyUnref(manager_proxy_);
+  }
+
+  // Initializes the DBus connector. Returns |true| on success.
+  bool Init();
+
+  // Obtains the default network connection, storing the connection status to
+  // |*is_connected_p| and (if connected) the service path for the default
+  // connection in |*default_service_path_p|. Returns |true| on success; |false|
+  // on failure, in which case no values are written.
+  bool GetDefaultConnection(bool* is_connected_p,
+                            std::string* default_service_path_p);
+
+  // Obtains the type of a network connection described by |service_path|,
+  // storing it to |*conn_type_p|. Returns |true| on success; |false| on
+  // failure, in which case no value is written.
+  bool GetConnectionType(const std::string& service_path,
+                         ShillConnType* conn_type_p);
+
+ private:
+  typedef struct {
+    const char *str;
+    ShillConnType type;
+  } ConnStrToType;
+
+  // A mapping from shill connection type strings to enum values.
+  static const ConnStrToType shill_conn_str_to_type[];
+
+  // The DBus interface and connection, and a shill manager proxy.
+  DBusWrapperInterface* dbus_;
+  DBusGConnection* connection_ = NULL;
+  DBusGProxy* manager_proxy_ = NULL;
+
+  // Return a DBus proxy for a given |path| and |interface| within shill.
+  DBusGProxy* GetProxy(const char* path, const char* interface);
+
+  // Converts a shill connection type string into a symbolic value.
+  ShillConnType ParseConnType(const char* str);
+
+  // Issues a GetProperties call through a given |proxy|, storing the result to
+  // |*result_p|. Returns |true| on success.
+  bool GetProperties(DBusGProxy* proxy, GHashTable** result_p);
+
+  DISALLOW_COPY_AND_ASSIGN(ShillConnector);
+};
+
 // ShillProvider concrete implementation.
-//
-// TODO(garnold) Much of the functionality in this module was adapted from
-// connection_manager, with slight changes (annotated inline).  We need to make
-// sure to deprecate use of connection manager when the time comes.
 class RealShillProvider : public ShillProvider {
  public:
-  // TODO(garnold) This should take a DBus object for communicating with shill.
-  RealShillProvider()
-      : is_connected_(false), conn_type_(kShillConnTypeUnknown),
-        conn_last_changed_(Time::Now()) {}
+  RealShillProvider(DBusWrapperInterface* dbus, ClockInterface* clock)
+      : conn_last_changed_(clock->GetWallclockTime()),
+        dbus_(dbus), clock_(clock),
+        is_connected_tracker_(clock, false, &conn_last_changed_) {}
 
  protected:
   virtual bool DoInit();
 
  private:
-  // Whether we have network connectivity.
-  bool is_connected_;
-
-  // The current network connection type as reported by shill.
-  ShillConnType conn_type_;
-
   // The time when the connection type last changed.
-  Time conn_last_changed_;
+  base::Time conn_last_changed_;
+
+  // A shill DBus connector.
+  scoped_ptr<ShillConnector> connector_;
+
+  // The DBus interface object (mockable).
+  DBusWrapperInterface* const dbus_;
+
+  // A clock abstraction (mockable).
+  ClockInterface* const clock_;
+
+  // Tracker for the latest connection status.
+  LastValueTracker<bool> is_connected_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(RealShillProvider);
 };
diff --git a/policy_manager/real_shill_provider_unittest.cc b/policy_manager/real_shill_provider_unittest.cc
index 6bc544c..ded5d63 100644
--- a/policy_manager/real_shill_provider_unittest.cc
+++ b/policy_manager/real_shill_provider_unittest.cc
@@ -4,38 +4,208 @@
 
 #include <base/memory/scoped_ptr.h>
 #include <base/time.h>
+#include <chromeos/dbus/service_constants.h>
+#include <glib.h>
 #include <gtest/gtest.h>
 
+#include "update_engine/fake_clock.h"
+#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/policy_manager/real_shill_provider.h"
 #include "update_engine/policy_manager/pmtest_utils.h"
 
 using base::Time;
 using base::TimeDelta;
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::MockDBusWrapper;
+using std::pair;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrEq;
+using testing::StrictMock;
+
+namespace {
+
+// Fake dbus-glib objects. These should be different values, to ease diagnosis
+// of errors.
+DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
+DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
+DBusGProxy* const kFakeEthernetServiceProxy = reinterpret_cast<DBusGProxy*>(3);
+DBusGProxy* const kFakeWifiServiceProxy = reinterpret_cast<DBusGProxy*>(4);
+DBusGProxy* const kFakeWimaxServiceProxy = reinterpret_cast<DBusGProxy*>(5);
+DBusGProxy* const kFakeBluetoothServiceProxy = reinterpret_cast<DBusGProxy*>(6);
+DBusGProxy* const kFakeCellularServiceProxy = reinterpret_cast<DBusGProxy*>(7);
+DBusGProxy* const kFakeVpnServiceProxy = reinterpret_cast<DBusGProxy*>(8);
+DBusGProxy* const kFakeUnknownServiceProxy = reinterpret_cast<DBusGProxy*>(9);
+
+// Fake service paths.
+const char* const kFakeEthernetServicePath = "/fake-ethernet-service";
+const char* const kFakeWifiServicePath = "/fake-wifi-service";
+const char* const kFakeWimaxServicePath = "/fake-wimax-service";
+const char* const kFakeBluetoothServicePath = "/fake-bluetooth-service";
+const char* const kFakeCellularServicePath = "/fake-cellular-service";
+const char* const kFakeVpnServicePath = "/fake-vpn-service";
+const char* const kFakeUnknownServicePath = "/fake-unknown-service";
+
+// Allocates, initializes and returns a string GValue object.
+GValue* GValueNewString(const char* str) {
+  GValue* gval = new GValue();
+  g_value_init(gval, G_TYPE_STRING);
+  g_value_set_string(gval, str);
+  return gval;
+}
+
+// Frees a GValue object and its allocated resources.
+void GValueFree(gpointer arg) {
+  auto gval = reinterpret_cast<GValue*>(arg);
+  g_value_unset(gval);
+  delete gval;
+}
+
+}  // namespace
 
 namespace chromeos_policy_manager {
 
 class PmRealShillProviderTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    default_timeout_ = TimeDelta::FromSeconds(1);
-
     // The provider initializes correctly.
-    time_before_init_ = Time::Now();
-    provider_.reset(new RealShillProvider());
-    time_after_init_ = Time::Now();
+    fake_clock_.SetWallclockTime(init_time_);
+    // A DBus connection should only be obtained once.
+    EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(Return(kFakeConnection));
+    provider_.reset(new RealShillProvider(&mock_dbus_, &fake_clock_));
     PMTEST_ASSERT_NOT_NULL(provider_.get());
+    // A manager proxy should only be obtained once.
+    EXPECT_CALL(mock_dbus_, ProxyNewForName(
+            kFakeConnection, StrEq(shill::kFlimflamServiceName),
+            StrEq(shill::kFlimflamServicePath),
+            StrEq(shill::kFlimflamManagerInterface)))
+        .WillOnce(Return(kFakeManagerProxy));
     ASSERT_TRUE(provider_->Init());
   }
 
-  TimeDelta default_timeout_;
-  Time time_before_init_;
-  Time time_after_init_;
+  virtual void TearDown() {
+    // Make sure the manager proxy gets unreffed (once).
+    EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy)).WillOnce(Return());
+    provider_.reset();
+    // Verify and clear all expectations.
+    testing::Mock::VerifyAndClear(&mock_dbus_);
+  }
+
+  // Sets up a mock "GetProperties" call on |proxy| that returns a hash table
+  // containing |num_entries| entries formed by subsequent key/value pairs. Keys
+  // and values are plain C strings (const char*). Mock will be expected to call
+  // exactly once, unless |allow_multiple_calls| is true, in which case it's
+  // allowed to be called multiple times. Returns a pointer to a newly allocated
+  // hash table, which should be unreffed with g_hash_table_unref() when done.
+  GHashTable* MockGetProperties(DBusGProxy* proxy, bool allow_multiple_calls,
+                                size_t num_entries,
+                                pair<const char*, const char*>* key_val_pairs) {
+    // Allocate and populate the hash table.
+    auto properties = g_hash_table_new_full(g_str_hash, g_str_equal, free,
+                                            GValueFree);
+    for (size_t i = 0; i < num_entries; i++) {
+      g_hash_table_insert(properties, strdup(key_val_pairs[i].first),
+                          GValueNewString(key_val_pairs[i].second));
+    }
+
+    // Set mock expectation and actions.
+    auto action = DoAll(SetArgPointee<3>(g_hash_table_ref(properties)),
+                        Return(true));
+    if (allow_multiple_calls) {
+      EXPECT_CALL(mock_dbus_, ProxyCall_0_1(
+              proxy, StrEq(shill::kGetPropertiesFunction), _, _))
+          .WillRepeatedly(action);
+    } else {
+      EXPECT_CALL(mock_dbus_, ProxyCall_0_1(
+              proxy, StrEq(shill::kGetPropertiesFunction), _, _))
+          .WillOnce(action);
+    }
+
+    return properties;
+  }
+
+  // Programs the mock DBus interface to return an non-VPN service and ensures
+  // that the shill provider reads it correctly, including updating the last
+  // changed time on each of the queries.
+  void SetConnectionAndTestType(const char* service_path,
+                                DBusGProxy* service_proxy,
+                                const char* shill_type_str,
+                                ShillConnType expected_conn_type) {
+    // Mock logic for returning a default service path.
+    pair<const char*, const char*> manager_pairs[] = {
+      {shill::kDefaultServiceProperty, service_path},
+    };
+    auto manager_properties = MockGetProperties(
+        kFakeManagerProxy, true, arraysize(manager_pairs), manager_pairs);
+
+    // Query the connection status, ensure last change time reported correctly.
+    const Time change_time = Time::Now();
+    fake_clock_.SetWallclockTime(change_time);
+    scoped_ptr<const bool> is_connected(
+        provider_->var_is_connected()->GetValue(default_timeout_, NULL));
+    PMTEST_ASSERT_NOT_NULL(is_connected.get());
+    EXPECT_TRUE(*is_connected);
+
+    scoped_ptr<const Time> conn_last_changed_1(
+        provider_->var_conn_last_changed()->GetValue(default_timeout_, NULL));
+    PMTEST_ASSERT_NOT_NULL(conn_last_changed_1.get());
+    EXPECT_EQ(change_time, *conn_last_changed_1);
+
+    // Mock logic for querying the type of the default service.
+    EXPECT_CALL(mock_dbus_, ProxyNewForName(
+            kFakeConnection, StrEq(shill::kFlimflamServiceName),
+            StrEq(service_path),
+            StrEq(shill::kFlimflamServiceInterface)))
+        .WillOnce(Return(service_proxy));
+    EXPECT_CALL(mock_dbus_, ProxyUnref(service_proxy))
+        .WillOnce(Return());
+    pair<const char*, const char*> service_pairs[] = {
+      {shill::kTypeProperty, shill_type_str},
+    };
+    auto service_properties = MockGetProperties(
+        service_proxy, false, arraysize(service_pairs),
+        service_pairs);
+
+    // Query the connection type, ensure last change time reported correctly.
+    fake_clock_.SetWallclockTime(change_time + TimeDelta::FromSeconds(5));
+    scoped_ptr<const ShillConnType> conn_type(
+        provider_->var_conn_type()->GetValue(default_timeout_, NULL));
+    PMTEST_ASSERT_NOT_NULL(conn_type.get());
+    EXPECT_EQ(expected_conn_type, *conn_type);
+
+    scoped_ptr<const Time> conn_last_changed_2(
+        provider_->var_conn_last_changed()->GetValue(default_timeout_, NULL));
+    PMTEST_ASSERT_NOT_NULL(conn_last_changed_2.get());
+    EXPECT_EQ(change_time, *conn_last_changed_2);
+
+    // Release properties hash tables.
+    g_hash_table_unref(service_properties);
+    g_hash_table_unref(manager_properties);
+  }
+
+  const TimeDelta default_timeout_ = TimeDelta::FromSeconds(1);
+  const Time init_time_ = Time::Now();
+  StrictMock<MockDBusWrapper> mock_dbus_;
+  FakeClock fake_clock_;
   scoped_ptr<RealShillProvider> provider_;
 };
 
+// Programs the mock DBus interface to indicate no default connection; this is
+// in line with the default values the variables should be initialized with, and
+// so the last changed time should not be updated. Also note that reading the
+// connection type should not induce getting of a per-service proxy, as no
+// default service is reported.
+TEST_F(PmRealShillProviderTest, ReadDefaultValues) {
+  // Mock logic for returning no default connection.
+  pair<const char*, const char*> manager_pairs[] = {
+    {shill::kDefaultServiceProperty, "/"},
+  };
+  auto manager_properties = MockGetProperties(
+      kFakeManagerProxy, true, arraysize(manager_pairs), manager_pairs);
 
-TEST_F(PmRealShillProviderTest, DefaultValues) {
-  // Tests that initial values were set correctly.
+  // Query the provider variables.
   scoped_ptr<const bool> is_connected(
       provider_->var_is_connected()->GetValue(default_timeout_, NULL));
   PMTEST_ASSERT_NOT_NULL(is_connected.get());
@@ -43,14 +213,100 @@
 
   scoped_ptr<const ShillConnType> conn_type(
       provider_->var_conn_type()->GetValue(default_timeout_, NULL));
-  PMTEST_ASSERT_NOT_NULL(conn_type.get());
-  EXPECT_EQ(kShillConnTypeUnknown, *conn_type);
+  PMTEST_ASSERT_NULL(conn_type.get());
 
   scoped_ptr<const Time> conn_last_changed(
       provider_->var_conn_last_changed()->GetValue(default_timeout_, NULL));
   PMTEST_ASSERT_NOT_NULL(conn_last_changed.get());
-  EXPECT_LE(time_before_init_, *conn_last_changed);
-  EXPECT_GE(time_after_init_, *conn_last_changed);
+  EXPECT_EQ(init_time_, *conn_last_changed);
+
+  // Release properties hash table.
+  g_hash_table_unref(manager_properties);
+}
+
+// Test that Ethernet connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaEthernet) {
+  SetConnectionAndTestType(kFakeEthernetServicePath,
+                           kFakeEthernetServiceProxy,
+                           shill::kTypeEthernet,
+                           kShillConnTypeEthernet);
+}
+
+// Test that Wifi connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaWifi) {
+  SetConnectionAndTestType(kFakeWifiServicePath,
+                           kFakeWifiServiceProxy,
+                           shill::kTypeWifi,
+                           kShillConnTypeWifi);
+}
+
+// Test that Wimax connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaWimax) {
+  SetConnectionAndTestType(kFakeWimaxServicePath,
+                           kFakeWimaxServiceProxy,
+                           shill::kTypeWimax,
+                           kShillConnTypeWimax);
+}
+
+// Test that Bluetooth connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaBluetooth) {
+  SetConnectionAndTestType(kFakeBluetoothServicePath,
+                           kFakeBluetoothServiceProxy,
+                           shill::kTypeBluetooth,
+                           kShillConnTypeBluetooth);
+}
+
+// Test that Cellular connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaCellular) {
+  SetConnectionAndTestType(kFakeCellularServicePath,
+                           kFakeCellularServiceProxy,
+                           shill::kTypeCellular,
+                           kShillConnTypeCellular);
+}
+
+// Test that an unknown connection is identified as such.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaUnknown) {
+  SetConnectionAndTestType(kFakeUnknownServicePath,
+                           kFakeUnknownServiceProxy,
+                           "FooConnectionType",
+                           kShillConnTypeUnknown);
+}
+
+// Tests that VPN connection is identified correctly.
+TEST_F(PmRealShillProviderTest, ReadChangedValuesConnectedViaVpn) {
+  // Mock logic for returning a default service path and its type.
+  pair<const char*, const char*> manager_pairs[] = {
+    {shill::kDefaultServiceProperty, kFakeVpnServicePath},
+  };
+  auto manager_properties = MockGetProperties(
+      kFakeManagerProxy, false, arraysize(manager_pairs), manager_pairs);
+  EXPECT_CALL(mock_dbus_, ProxyNewForName(
+          kFakeConnection, StrEq(shill::kFlimflamServiceName),
+          StrEq(kFakeVpnServicePath), StrEq(shill::kFlimflamServiceInterface)))
+      .WillOnce(Return(kFakeVpnServiceProxy));
+  EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeVpnServiceProxy)).WillOnce(Return());
+  pair<const char*, const char*> service_pairs[] = {
+    {shill::kTypeProperty, shill::kTypeVPN},
+    {shill::kPhysicalTechnologyProperty, shill::kTypeWifi},
+  };
+  auto service_properties = MockGetProperties(
+      kFakeVpnServiceProxy, false, arraysize(service_pairs), service_pairs);
+
+  // Query the connection type, ensure last change time reported correctly.
+  fake_clock_.SetWallclockTime(Time::Now());
+  scoped_ptr<const ShillConnType> conn_type(
+      provider_->var_conn_type()->GetValue(default_timeout_, NULL));
+  PMTEST_ASSERT_NOT_NULL(conn_type.get());
+  EXPECT_EQ(kShillConnTypeWifi, *conn_type);
+
+  scoped_ptr<const Time> conn_last_changed(
+      provider_->var_conn_last_changed()->GetValue(default_timeout_, NULL));
+  PMTEST_ASSERT_NOT_NULL(conn_last_changed.get());
+  EXPECT_EQ(fake_clock_.GetWallclockTime(), *conn_last_changed);
+
+  // Release properties hash tables.
+  g_hash_table_unref(service_properties);
+  g_hash_table_unref(manager_properties);
 }
 
 }  // namespace chromeos_policy_manager
diff --git a/policy_manager/real_state.cc b/policy_manager/real_state.cc
index 90b0b3d..4e140d6 100644
--- a/policy_manager/real_state.cc
+++ b/policy_manager/real_state.cc
@@ -2,15 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "update_engine/policy_manager/real_state.h"
+
 #include "update_engine/policy_manager/real_random_provider.h"
 #include "update_engine/policy_manager/real_shill_provider.h"
-#include "update_engine/policy_manager/real_state.h"
 
 namespace chromeos_policy_manager {
 
-RealState::RealState() {
+// TODO(garnold) We should be injecting actual provider objects here.
+RealState::RealState(chromeos_update_engine::DBusWrapperInterface* dbus,
+                     chromeos_update_engine::ClockInterface* clock) {
   set_random_provider(new RealRandomProvider());
-  set_shill_provider(new RealShillProvider());
+  set_shill_provider(new RealShillProvider(dbus, clock));
 }
 
 }  // namespace chromeos_policy_manager
diff --git a/policy_manager/real_state.h b/policy_manager/real_state.h
index b46126e..808a692 100644
--- a/policy_manager/real_state.h
+++ b/policy_manager/real_state.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_REAL_STATE_H
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_POLICY_MANAGER_REAL_STATE_H
 
+#include "update_engine/clock_interface.h"
+#include "update_engine/dbus_wrapper_interface.h"
 #include "update_engine/policy_manager/state.h"
 
 namespace chromeos_policy_manager {
@@ -12,7 +14,8 @@
 // State implementation class.
 class RealState : public State {
  public:
-  RealState();
+  RealState(chromeos_update_engine::DBusWrapperInterface* dbus,
+            chromeos_update_engine::ClockInterface* clock);
   ~RealState() {}
 
  private:
diff --git a/policy_manager/real_state_unittest.cc b/policy_manager/real_state_unittest.cc
index c0249d9..48491d5 100644
--- a/policy_manager/real_state_unittest.cc
+++ b/policy_manager/real_state_unittest.cc
@@ -4,13 +4,30 @@
 
 #include <gtest/gtest.h>
 
+#include "update_engine/fake_clock.h"
+#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/policy_manager/real_state.h"
 #include "update_engine/policy_manager/pmtest_utils.h"
 
+using chromeos_update_engine::FakeClock;
+using chromeos_update_engine::MockDBusWrapper;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+namespace {
+
+DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
+
+}  // namespace
+
 namespace chromeos_policy_manager {
 
 TEST(PmRealStateTest, InitTest) {
-  RealState state;
+  NiceMock<MockDBusWrapper> mock_dbus;
+  FakeClock fake_clock;
+  EXPECT_CALL(mock_dbus, BusGet(_, _)).WillOnce(Return(kFakeConnection));
+  RealState state(&mock_dbus, &fake_clock);
   EXPECT_TRUE(state.Init());
   // Check that the providers are being initialized.
   PMTEST_ASSERT_NOT_NULL(state.random_provider());
diff --git a/policy_manager/shill_provider.h b/policy_manager/shill_provider.h
index 12f15a2..4423f78 100644
--- a/policy_manager/shill_provider.h
+++ b/policy_manager/shill_provider.h
@@ -11,8 +11,6 @@
 #include "update_engine/policy_manager/provider.h"
 #include "update_engine/policy_manager/variable.h"
 
-using base::Time;
-
 namespace chromeos_policy_manager {
 
 // TODO(garnold) Adapted from connection_manager.h.
diff --git a/policy_manager/variable.h b/policy_manager/variable.h
index 28a9fb2..822ab3b 100644
--- a/policy_manager/variable.h
+++ b/policy_manager/variable.h
@@ -141,7 +141,9 @@
   friend class PmRealRandomProviderTest;
   FRIEND_TEST(PmRealRandomProviderTest, GetRandomValues);
   friend class PmRealShillProviderTest;
-  FRIEND_TEST(PmRealShillProviderTest, DefaultValues);
+  FRIEND_TEST(PmRealShillProviderTest, ReadDefaultValues);
+  FRIEND_TEST(PmRealShillProviderTest, ReadChangedValuesConnectedViaEthernet);
+  FRIEND_TEST(PmRealShillProviderTest, ReadChangedValuesConnectedViaVpn);
 
   Variable(const std::string& name, VariableMode mode)
       : BaseVariable(name, mode) {}