[shill] Enable default profile to persist device info

Make Profile::Save() take a pointer to a vector of DeviceRefPtrs.  The base
implementation ignores this pointer, but the DefaultProfile implementation
persists all the devices to disk.

BUG=chromium-os:17254
TEST=unit
STATUS=Verified

Change-Id: I5d72bd2319edfb9ae57366cbd5c766b558ffc8a4
Reviewed-on: http://gerrit.chromium.org/gerrit/8057
Commit-Ready: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
diff --git a/default_profile.cc b/default_profile.cc
index 0c8d51f..f8b263b 100644
--- a/default_profile.cc
+++ b/default_profile.cc
@@ -4,6 +4,8 @@
 
 #include "shill/default_profile.h"
 
+#include <vector>
+
 #include <base/file_path.h>
 #include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
@@ -13,6 +15,8 @@
 #include "shill/manager.h"
 #include "shill/store_interface.h"
 
+using std::vector;
+
 namespace shill {
 // static
 const char DefaultProfile::kDefaultId[] = "default";
@@ -51,6 +55,13 @@
   storage->SetString(kStorageId,
                      kStorageCheckPortalList,
                      props_.check_portal_list);
+  vector<DeviceRefPtr>::iterator it;
+  for (it = manager()->devices_begin(); it != manager()->devices_end(); ++it) {
+    if (!(*it)->Save(storage)) {
+      LOG(ERROR) << "Could not save " << (*it)->UniqueName();
+      return false;
+    }
+  }
   return Profile::Save(storage);
 }
 
diff --git a/default_profile.h b/default_profile.h
index 59a75c3..d362f48 100644
--- a/default_profile.h
+++ b/default_profile.h
@@ -30,6 +30,9 @@
                  const Manager::Properties &manager_props);
   virtual ~DefaultProfile();
 
+  // Persists profile information, as well as that of discovered devices
+  // and bound services, to disk.
+  // Returns true on success, false on failure.
   virtual bool Save(StoreInterface *storage);
 
   // Sets |path| to the persistent store file path for the default, global
diff --git a/default_profile_unittest.cc b/default_profile_unittest.cc
index 84ecc16..f920e9b 100644
--- a/default_profile_unittest.cc
+++ b/default_profile_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <map>
 #include <string>
+#include <vector>
 
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
@@ -13,11 +14,13 @@
 
 #include "shill/manager.h"
 #include "shill/mock_control.h"
+#include "shill/mock_device.h"
 #include "shill/mock_store.h"
 #include "shill/property_store_unittest.h"
 
 using std::map;
 using std::string;
+using std::vector;
 using ::testing::_;
 using ::testing::Return;
 
@@ -29,7 +32,13 @@
       : profile_(new DefaultProfile(control_interface(),
                                     manager(),
                                     FilePath(kTestStoragePath),
-                                    properties_)) {
+                                    properties_)),
+        device_(new MockDevice(control_interface(),
+                               dispatcher(),
+                               manager(),
+                               "null0",
+                               "addr0",
+                               0)) {
   }
 
   virtual ~DefaultProfileTest() {}
@@ -38,6 +47,7 @@
   static const char kTestStoragePath[];
 
   ProfileRefPtr profile_;
+  scoped_refptr<MockDevice> device_;
   Manager::Properties properties_;
 };
 
@@ -84,7 +94,12 @@
                                DefaultProfile::kStorageOfflineMode,
                                false))
       .WillOnce(Return(true));
+
+  EXPECT_CALL(*device_.get(), Save(&storage)).WillOnce(Return(true));
+
+  manager()->RegisterDevice(device_);
   ASSERT_TRUE(profile_->Save(&storage));
+  manager()->DeregisterDevice(device_);
 }
 
 TEST_F(DefaultProfileTest, GetStoragePath) {
diff --git a/device.h b/device.h
index 0ab72a9..7574e4e 100644
--- a/device.h
+++ b/device.h
@@ -97,7 +97,7 @@
   RTNLHandler *rtnl_handler() { return rtnl_handler_; }
 
   bool Load(StoreInterface *storage);
-  bool Save(StoreInterface *storage);
+  virtual bool Save(StoreInterface *storage);
 
   void set_dhcp_provider(DHCPProvider *provider) { dhcp_provider_ = provider; }
 
diff --git a/device_info.cc b/device_info.cc
index 611e039..3517a96 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -21,6 +21,7 @@
 #include <base/logging.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/stl_util-inl.h>
+#include <base/string_util.h>
 #include <base/stringprintf.h>
 
 #include "shill/control_interface.h"
@@ -186,7 +187,8 @@
         technology = GetDeviceTechnology(link_name);
       }
     }
-    string address = infos_[dev_index].mac_address.HexEncode();
+    string address =
+        StringToLowerASCII(infos_[dev_index].mac_address.HexEncode());
     switch (technology) {
       case Device::kCellular:
         // Cellular devices are managed by ModemInfo.
diff --git a/manager.h b/manager.h
index 1104aca..6bddeb5 100644
--- a/manager.h
+++ b/manager.h
@@ -75,6 +75,11 @@
   PropertyStore *mutable_store() { return &store_; }
   virtual const PropertyStore &store() const { return store_; }
 
+  std::vector<DeviceRefPtr>::iterator devices_begin() {
+    return devices_.begin();
+  }
+  std::vector<DeviceRefPtr>::iterator devices_end() { return devices_.end(); }
+
  private:
   friend class ManagerAdaptorInterface;
 
diff --git a/mock_device.h b/mock_device.h
index 4c3fa9b..d86c07c 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -14,9 +14,6 @@
 
 namespace shill {
 
-class ControlInterface;
-class EventDispatcher;
-
 class MockDevice : public Device {
  public:
   MockDevice(ControlInterface *control_interface,
@@ -31,6 +28,7 @@
   MOCK_METHOD0(Stop, void());
   MOCK_METHOD1(Scan, void(Error *error));
   MOCK_CONST_METHOD1(TechnologyIs, bool(const Technology technology));
+  MOCK_METHOD1(Save, bool(StoreInterface*));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDevice);