shill: DeviceInfo::GetAddress can be used now to lookup the device MAC address.

Also enhance ByteString with a HexEncode method used for logging.

BUG=chromium-os:19028
TEST=unit tests, tested on device

Change-Id: Ie64ebb0d009ce7fb3ed16948a7789ef721b56039
Reviewed-on: http://gerrit.chromium.org/gerrit/5766
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/byte_string.cc b/byte_string.cc
index 3c9db10..9976e0f 100644
--- a/byte_string.cc
+++ b/byte_string.cc
@@ -6,6 +6,10 @@
 
 #include <netinet/in.h>
 
+#include <base/string_number_conversions.h>
+
+using std::string;
+
 namespace shill {
 
 // static
@@ -53,4 +57,8 @@
   data_.insert(data_.end(), b.data_.begin(), b.data_.end());
 }
 
+string ByteString::HexEncode() const {
+  return base::HexEncode(GetConstData(), GetLength());
+}
+
 }  // namespace shill
diff --git a/byte_string.h b/byte_string.h
index 0077ff0..22ac750 100644
--- a/byte_string.h
+++ b/byte_string.h
@@ -48,6 +48,7 @@
   // Returns true on success
   bool ConvertToNetUInt32(uint32 *val) const;
 
+  bool IsEmpty() const { return GetLength() == 0; }
   bool IsZero() const;
   bool Equals(const ByteString &b) const;
   void Append(const ByteString &b);
@@ -55,6 +56,8 @@
     data_.resize(size, 0);
   }
 
+  std::string HexEncode() const;
+
  private:
   std::vector<unsigned char> data_;
   // NO DISALLOW_COPY_AND_ASSIGN -- we assign ByteStrings in STL hashes
diff --git a/byte_string_unittest.cc b/byte_string_unittest.cc
index e8e6048..fb35c34 100644
--- a/byte_string_unittest.cc
+++ b/byte_string_unittest.cc
@@ -14,7 +14,9 @@
 
 namespace {
 const unsigned char kTest1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-const unsigned char kTest2[] = { 1, 2, 3, 4 };
+const unsigned char kTest2[] = { 1, 2, 3, 0xa };
+const char kTest2HexString[] = "0102030A";
+const unsigned int kTest2Uint32 = 0x0102030a;
 const unsigned char kTest3[] = { 0, 0, 0, 0 };
 const char kTest4[] = "Hello world";
 }  // namespace {}
@@ -26,6 +28,7 @@
   uint32 val;
 
   ByteString bs1(0);
+  EXPECT_TRUE(bs1.IsEmpty());
   EXPECT_EQ(0, bs1.GetLength());
   EXPECT_TRUE(bs1.GetData() == NULL);
   EXPECT_FALSE(bs1.ConvertToNetUInt32(&val));
@@ -36,6 +39,8 @@
   ByteString bs1(kTest1, sizeof(kTest1));
   uint32 val;
 
+  EXPECT_FALSE(bs1.IsEmpty());
+
   EXPECT_EQ(sizeof(kTest1), bs1.GetLength());
   for (unsigned int i = 0; i < sizeof(kTest1); i++) {
     EXPECT_EQ(bs1.GetData()[i], kTest1[i]);
@@ -77,19 +82,19 @@
 }
 
 TEST_F(ByteStringTest, UInt32) {
-  ByteString bs1 = ByteString::CreateFromNetUInt32(0x1020304);
+  ByteString bs1 = ByteString::CreateFromNetUInt32(kTest2Uint32);
   uint32 val;
 
   EXPECT_EQ(4, bs1.GetLength());
   EXPECT_TRUE(bs1.GetData() != NULL);
   EXPECT_TRUE(bs1.ConvertToNetUInt32(&val));
-  EXPECT_EQ(0x1020304, val);
+  EXPECT_EQ(kTest2Uint32, val);
   EXPECT_FALSE(bs1.IsZero());
 
   ByteString bs2(kTest2, sizeof(kTest2));
   EXPECT_TRUE(bs1.Equals(bs2));
   EXPECT_TRUE(bs2.ConvertToNetUInt32(&val));
-  EXPECT_EQ(0x1020304, val);
+  EXPECT_EQ(kTest2Uint32, val);
 
   ByteString bs3 = ByteString::CreateFromCPUUInt32(0x1020304);
   EXPECT_EQ(4, bs1.GetLength());
@@ -122,4 +127,9 @@
   EXPECT_EQ(0, memcmp(bs1.GetData(), kTest2, sizeof(kTest2) - 2));
 }
 
+TEST_F(ByteStringTest, HexEncode) {
+  ByteString bs(kTest2, sizeof(kTest2));
+  EXPECT_EQ(kTest2HexString, bs.HexEncode());
+}
+
 }  // namespace shill
diff --git a/device_info.cc b/device_info.cc
index d6a680b..c6ba786 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -149,6 +149,11 @@
 
   DeviceRefPtr device = GetDevice(dev_index);
   if (!device.get()) {
+    if (msg.HasAttribute(IFLA_ADDRESS)) {
+      infos_[dev_index].address = msg.GetAttribute(IFLA_ADDRESS);
+      VLOG(2) << "link index " << dev_index << " address "
+              << infos_[dev_index].address.HexEncode();
+    }
     if (!msg.HasAttribute(IFLA_IFNAME)) {
       LOG(ERROR) << "Add Link message does not have IFLA_IFNAME!";
       return;
@@ -195,13 +200,22 @@
   RemoveInfo(msg.interface_index());
 }
 
-DeviceRefPtr DeviceInfo::GetDevice(int interface_index) {
-  Info *info = GetInfo(interface_index);
+DeviceRefPtr DeviceInfo::GetDevice(int interface_index) const {
+  const Info *info = GetInfo(interface_index);
   return info ? info->device : NULL;
 }
 
-bool DeviceInfo::GetFlags(int interface_index, unsigned int *flags) {
-  Info *info = GetInfo(interface_index);
+bool DeviceInfo::GetAddress(int interface_index, ByteString *address) const {
+  const Info *info = GetInfo(interface_index);
+  if (!info) {
+    return false;
+  }
+  *address = info->address;
+  return true;
+}
+
+bool DeviceInfo::GetFlags(int interface_index, unsigned int *flags) const {
+  const Info *info = GetInfo(interface_index);
   if (!info) {
     return false;
   }
@@ -209,8 +223,8 @@
   return true;
 }
 
-DeviceInfo::Info *DeviceInfo::GetInfo(int interface_index) {
-  map<int, Info>::iterator iter = infos_.find(interface_index);
+const DeviceInfo::Info *DeviceInfo::GetInfo(int interface_index) const {
+  map<int, Info>::const_iterator iter = infos_.find(interface_index);
   if (iter == infos_.end()) {
     return NULL;
   }
diff --git a/device_info.h b/device_info.h
index cb8c39e..3f94184 100644
--- a/device_info.h
+++ b/device_info.h
@@ -13,8 +13,8 @@
 #include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
 
+#include "shill/byte_string.h"
 #include "shill/device.h"
-
 #include "shill/rtnl_listener.h"
 
 namespace shill {
@@ -37,8 +37,9 @@
   // messages, and registers it with the manager.
   void RegisterDevice(const DeviceRefPtr &device);
 
-  DeviceRefPtr GetDevice(int interface_index);
-  bool GetFlags(int interface_index, unsigned int *flags);
+  DeviceRefPtr GetDevice(int interface_index) const;
+  bool GetAddress(int interface_index, ByteString *address) const;
+  bool GetFlags(int interface_index, unsigned int *flags) const;
 
  private:
   friend class DeviceInfoTest;
@@ -47,6 +48,7 @@
     Info() : flags(0) {}
 
     DeviceRefPtr device;
+    ByteString address;
     unsigned int flags;
   };
 
@@ -60,7 +62,7 @@
   void DelLinkMsgHandler(const RTNLMessage &msg);
   void LinkMsgHandler(const RTNLMessage &msg);
 
-  Info *GetInfo(int interface_index);
+  const Info *GetInfo(int interface_index) const;
   void RemoveInfo(int interface_index);
 
   ControlInterface *control_interface_;
diff --git a/device_info_unittest.cc b/device_info_unittest.cc
index f0247b9..3e76418 100644
--- a/device_info_unittest.cc
+++ b/device_info_unittest.cc
@@ -52,6 +52,7 @@
  protected:
   static const int kTestDeviceIndex;
   static const char kTestDeviceName[];
+  static const char kTestAddress[];
 
   RTNLMessage *BuildMessage(RTNLMessage::MessageType type,
                             RTNLMessage::MessageMode mode);
@@ -66,6 +67,8 @@
 
 const int DeviceInfoTest::kTestDeviceIndex = 123456;
 const char DeviceInfoTest::kTestDeviceName[] = "test-device";
+const char DeviceInfoTest::kTestAddress[] = {
+  0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
 
 RTNLMessage *DeviceInfoTest::BuildMessage(RTNLMessage::MessageType type,
                                           RTNLMessage::MessageMode mode) {
@@ -86,12 +89,18 @@
   scoped_ptr<RTNLMessage> message(BuildMessage(RTNLMessage::kMessageTypeLink,
                                                RTNLMessage::kMessageModeAdd));
   message->set_link_status(RTNLMessage::LinkStatus(0, IFF_LOWER_UP, 0));
+  ByteString test_address(kTestAddress, sizeof(kTestAddress));
+  message->SetAttribute(IFLA_ADDRESS, test_address);
   EXPECT_FALSE(device_info_.GetDevice(kTestDeviceIndex).get());
   SendMessageToDeviceInfo(*message);
   EXPECT_TRUE(device_info_.GetDevice(kTestDeviceIndex).get());
   unsigned int flags = 0;
   EXPECT_TRUE(device_info_.GetFlags(kTestDeviceIndex, &flags));
   EXPECT_EQ(IFF_LOWER_UP, flags);
+  ByteString address;
+  EXPECT_TRUE(device_info_.GetAddress(kTestDeviceIndex, &address));
+  EXPECT_FALSE(address.IsEmpty());
+  EXPECT_TRUE(test_address.Equals(address));
 
   message.reset(BuildMessage(RTNLMessage::kMessageTypeLink,
                              RTNLMessage::kMessageModeAdd));