[shill] Enable Device objects to persist themselves to disk

BUG=chromium-os:17254
TEST=unit

Change-Id: Ia00bc2658e0fe03e13e399d7afab81cc09aa0195
Reviewed-on: http://gerrit.chromium.org/gerrit/5309
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/control_interface.h b/control_interface.h
index 680ac63..4e5204e 100644
--- a/control_interface.h
+++ b/control_interface.h
@@ -5,6 +5,11 @@
 #ifndef SHILL_CONTROL_INTERFACE_
 #define SHILL_CONTROL_INTERFACE_
 
+#include <algorithm>
+#include <string>
+
+#include <base/logging.h>
+
 namespace shill {
 
 class Device;
@@ -28,6 +33,11 @@
   virtual ManagerAdaptorInterface *CreateManagerAdaptor(Manager *manager) = 0;
   virtual ProfileAdaptorInterface *CreateProfileAdaptor(Profile *profile) = 0;
   virtual ServiceAdaptorInterface *CreateServiceAdaptor(Service *service) = 0;
+
+  static void RpcIdToStorageId(std::string *rpc_id) {
+    CHECK(rpc_id);
+    std::replace(rpc_id->begin(), rpc_id->end(), '/', '_');
+  }
 };
 
 }  // namespace shill
diff --git a/device.cc b/device.cc
index fed0824..51615b9 100644
--- a/device.cc
+++ b/device.cc
@@ -12,6 +12,7 @@
 
 #include <base/logging.h>
 #include <base/memory/ref_counted.h>
+#include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
 
 #include "shill/control_interface.h"
@@ -25,11 +26,20 @@
 #include "shill/rtnl_handler.h"
 #include "shill/service.h"
 #include "shill/shill_event.h"
+#include "shill/store_interface.h"
 
+using base::StringPrintf;
 using std::string;
 using std::vector;
 
 namespace shill {
+
+// static
+const char Device::kStoragePowered[] = "Powered";
+
+// static
+const char Device::kStorageIPConfigs[] = "IPConfigs";
+
 Device::Device(ControlInterface *control_interface,
                EventDispatcher *dispatcher,
                Manager *manager,
@@ -120,6 +130,12 @@
   return adaptor_->GetRpcIdentifier();
 }
 
+string Device::GetStorageIdentifier() {
+  string id = GetRpcIdentifier();
+  ControlInterface::RpcIdToStorageId(&id);
+  return id;
+}
+
 const string& Device::FriendlyName() const {
   return link_name_;
 }
@@ -128,6 +144,25 @@
   return unique_id_;
 }
 
+bool Device::Load(StoreInterface *storage) {
+  const string id = GetStorageIdentifier();
+  if (!storage->ContainsGroup(id)) {
+    LOG(WARNING) << "Device is not available in the persistent store: " << id;
+    return false;
+  }
+  storage->GetBool(id, kStoragePowered, &powered_);
+  // TODO(cmasone): What does it mean to load an IPConfig identifier??
+  return true;
+}
+
+bool Device::Save(StoreInterface *storage) {
+  const string id = GetStorageIdentifier();
+  storage->SetBool(id, kStoragePowered, powered_);
+  if (ipconfig_.get())
+    storage->SetString(id, kStorageIPConfigs, SerializeIPConfigsForStorage());
+  return true;
+}
+
 void Device::DestroyIPConfig() {
   if (ipconfig_.get()) {
     RTNLHandler::GetInstance()->RemoveInterfaceAddress(interface_index_,
@@ -164,6 +199,12 @@
   }
 }
 
+string Device::SerializeIPConfigsForStorage() {
+  return StringPrintf("%s:%s",
+                      ipconfig_->GetStorageIdentifier().c_str(),
+                      ipconfig_->type().c_str());
+}
+
 vector<string> Device::AvailableIPConfigs() {
   string id = (ipconfig_.get() ? ipconfig_->GetRpcIdentifier() : string());
   return vector<string>(1, id);
diff --git a/device.h b/device.h
index 069b158..08548e6 100644
--- a/device.h
+++ b/device.h
@@ -61,6 +61,7 @@
   virtual void ConfigIP() {}
 
   std::string GetRpcIdentifier();
+  std::string GetStorageIdentifier();
 
   const std::string &link_name() const { return link_name_; }
   int interface_index() const { return interface_index_; }
@@ -73,11 +74,15 @@
 
   PropertyStore *store() { return &store_; }
 
+  bool Load(StoreInterface *storage);
+  bool Save(StoreInterface *storage);
+
  protected:
   FRIEND_TEST(DeviceTest, AcquireDHCPConfig);
   FRIEND_TEST(DeviceTest, DestroyIPConfig);
   FRIEND_TEST(DeviceTest, DestroyIPConfigNULL);
   FRIEND_TEST(DeviceTest, GetProperties);
+  FRIEND_TEST(DeviceTest, Save);
 
   // If there's an IP configuration in |ipconfig_|, releases the IP address and
   // destroys the configuration instance.
@@ -111,9 +116,17 @@
  private:
   friend class DeviceAdaptorInterface;
 
+  static const char kStoragePowered[];
+  static const char kStorageIPConfigs[];
+
   // Callback invoked on every IP configuration update.
   void IPConfigUpdatedCallback(const IPConfigRefPtr &ipconfig, bool success);
 
+  // Right now, Devices reference IPConfigs directly when persisted to disk
+  // It's not clear that this makes sense long-term, but that's how it is now.
+  // This call generates a string in the right format for this persisting.
+  std::string SerializeIPConfigsForStorage();
+
   std::vector<std::string> AvailableIPConfigs();
   std::string GetRpcConnectionIdentifier();
 
diff --git a/device_unittest.cc b/device_unittest.cc
index 8d5c2cc..8142277 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -20,6 +20,7 @@
 #include "shill/mock_control.h"
 #include "shill/mock_device.h"
 #include "shill/mock_glib.h"
+#include "shill/mock_store.h"
 #include "shill/property_store_unittest.h"
 #include "shill/shill_event.h"
 
@@ -27,6 +28,7 @@
 using std::string;
 using std::vector;
 using ::testing::_;
+using ::testing::AtLeast;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::Test;
@@ -122,4 +124,27 @@
   EXPECT_TRUE(device_->ipconfig_->update_callback_.get());
 }
 
+TEST_F(DeviceTest, Load) {
+  NiceMock<MockStore> storage;
+  const string id = device_->GetStorageIdentifier();
+  EXPECT_CALL(storage, ContainsGroup(id)).WillOnce(Return(true));
+  EXPECT_CALL(storage, GetBool(id, _, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(true));
+  EXPECT_TRUE(device_->Load(&storage));
+}
+
+TEST_F(DeviceTest, Save) {
+  NiceMock<MockStore> storage;
+  const string id = device_->GetStorageIdentifier();
+  device_->ipconfig_ = new IPConfig(&control_interface_, kDeviceName);
+  EXPECT_CALL(storage, SetString(id, _, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(storage, SetBool(id, _, _))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(true));
+  EXPECT_TRUE(device_->Save(&storage));
+}
+
 }  // namespace shill
diff --git a/ipconfig.cc b/ipconfig.cc
index cb652f9..8b45703 100644
--- a/ipconfig.cc
+++ b/ipconfig.cc
@@ -4,8 +4,6 @@
 
 #include "shill/ipconfig.h"
 
-#include <algorithm>
-
 #include <base/logging.h>
 #include <chromeos/dbus/service_constants.h>
 
@@ -19,7 +17,7 @@
 namespace shill {
 
 // static
-const char IPConfig::kStorageType[] = "Mode";
+const char IPConfig::kStorageType[] = "Method";
 // static
 const char IPConfig::kType[] = "ip";
 // static
@@ -74,6 +72,12 @@
   return adaptor_->GetRpcIdentifier();
 }
 
+string IPConfig::GetStorageIdentifier() {
+  string id = GetRpcIdentifier();
+  ControlInterface::RpcIdToStorageId(&id);
+  return id;
+}
+
 bool IPConfig::RequestIP() {
   return false;
 }
@@ -115,10 +119,4 @@
   update_callback_.reset(callback);
 }
 
-string IPConfig::GetStorageIdentifier() {
-  string id = adaptor_->GetRpcIdentifier();
-  std::replace(id.begin(), id.end(), '/', '_');
-  return id;
-}
-
 }  // namespace shill
diff --git a/ipconfig.h b/ipconfig.h
index 8feee05..de4f32b 100644
--- a/ipconfig.h
+++ b/ipconfig.h
@@ -58,6 +58,7 @@
   uint serial() const { return serial_; }
 
   std::string GetRpcIdentifier();
+  std::string GetStorageIdentifier();
 
   // Registers a callback that's executed every time the configuration
   // properties change. Takes ownership of |callback|. Pass NULL to remove a
@@ -109,7 +110,6 @@
   scoped_ptr<Callback2<const IPConfigRefPtr&, bool>::Type> update_callback_;
 
   void Init();
-  std::string GetStorageIdentifier();
 
   DISALLOW_COPY_AND_ASSIGN(IPConfig);
 };