[shill] Enable objects to query an opaque RPC-system ID from Adaptors

This is needed for some properties.  For example Service has a property called Device, which is expected
to return a (in the current impl) a DBus path for the associated Device object.

BUG=chromium-os:16343
TEST=unit tests

Change-Id: I8bd32ab483331efabbfee05dbdeba045c7cb20da
Reviewed-on: http://gerrit.chromium.org/gerrit/3417
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/Makefile b/Makefile
index 6369018..b3f8a2d 100644
--- a/Makefile
+++ b/Makefile
@@ -94,6 +94,7 @@
 	ipconfig_unittest.o \
 	key_file_store_unittest.o \
 	manager_unittest.o \
+	mock_adaptors.o \
 	mock_control.o \
 	mock_device.o \
 	mock_service.o \
diff --git a/adaptor_interfaces.h b/adaptor_interfaces.h
index 6116e19..4c4a7e1 100644
--- a/adaptor_interfaces.h
+++ b/adaptor_interfaces.h
@@ -15,6 +15,11 @@
 class ManagerAdaptorInterface {
  public:
   virtual ~ManagerAdaptorInterface() {}
+
+  // Getter for the opaque identifier that represents this object on the
+  // RPC interface to which the implementation is adapting.
+  virtual const std::string &GetRpcIdentifier() = 0;
+
   virtual void UpdateRunning() = 0;
 
   virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
@@ -30,6 +35,11 @@
 class ServiceAdaptorInterface {
  public:
   virtual ~ServiceAdaptorInterface() {}
+
+  // Getter for the opaque identifier that represents this object on the
+  // RPC interface to which the implementation is adapting.
+  virtual const std::string &GetRpcIdentifier() = 0;
+
   virtual void UpdateConnected() = 0;
 
   virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
@@ -44,6 +54,10 @@
  public:
   virtual ~DeviceAdaptorInterface() {}
 
+  // Getter for the opaque identifier that represents this object on the
+  // RPC interface to which the implementation is adapting.
+  virtual const std::string &GetRpcIdentifier() = 0;
+
   virtual void UpdateEnabled() = 0;
 
   virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
diff --git a/cellular_service.cc b/cellular_service.cc
index 70783c1..6cb4095 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -52,4 +52,8 @@
 
 void CellularService::Disconnect() { }
 
+std::string CellularService::GetDeviceRpcId() {
+  return cellular_->GetRpcIdentifier();
+}
+
 }  // namespace shill
diff --git a/cellular_service.h b/cellular_service.h
index f748377..bcb003c 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -10,7 +10,6 @@
 
 #include <base/basictypes.h>
 
-#include "shill/cellular.h"
 #include "shill/refptr_types.h"
 #include "shill/shill_event.h"
 #include "shill/service.h"
@@ -43,6 +42,8 @@
   std::map<std::string, std::string> last_good_apn_info_;
 
  private:
+  std::string GetDeviceRpcId();
+
   CellularRefPtr cellular_;
   const std::string type_;
   DISALLOW_COPY_AND_ASSIGN(CellularService);
diff --git a/device.cc b/device.cc
index 428f985..447adde 100644
--- a/device.cc
+++ b/device.cc
@@ -21,6 +21,7 @@
 #include "shill/dhcp_provider.h"
 #include "shill/error.h"
 #include "shill/manager.h"
+#include "shill/property_accessor.h"
 #include "shill/refptr_types.h"
 #include "shill/rtnl_handler.h"
 #include "shill/service.h"
@@ -44,6 +45,9 @@
       running_(false) {
 
   RegisterConstString(flimflam::kAddressProperty, &hardware_address_);
+  RegisterDerivedString(flimflam::kDBusObjectProperty,
+                        &Device::GetRpcIdentifier,
+                        NULL);
   // TODO(cmasone): Chrome doesn't use this...does anyone?
   // RegisterConstString(flimflam::kInterfaceProperty, &link_name_);
   RegisterConstString(flimflam::kNameProperty, &link_name_);
@@ -53,7 +57,6 @@
 
   // TODO(cmasone): Add support for R/O properties that return DBus object paths
   // known_properties_.push_back(flimflam::kDBusConnectionProperty);
-  // known_properties_.push_back(flimflam::kDBusObjectProperty);
   // known_properties_.push_back(flimflam::kIPConfigsProperty);
   // known_properties_.push_back(flimflam::kNetworksProperty);
 
@@ -136,6 +139,10 @@
   return link_name();
 }
 
+string Device::GetRpcIdentifier() {
+  return adaptor_->GetRpcIdentifier();
+}
+
 void Device::DestroyIPConfig() {
   if (ipconfig_.get()) {
     RTNLHandler::GetInstance()->RemoveInterfaceAddress(interface_index_,
@@ -153,6 +160,13 @@
   return ipconfig_->RequestIP();
 }
 
+void Device::RegisterDerivedString(const string &name,
+                                    string(Device::*get)(void),
+                                    bool(Device::*set)(const string&)) {
+  string_properties_[name] =
+      StringAccessor(new CustomAccessor<Device, string>(this, get, set));
+}
+
 void Device::IPConfigUpdatedCallback(const IPConfigRefPtr &ipconfig,
                                      bool success) {
   // TODO(petkov): Use DeviceInfo to configure IP, etc. -- maybe through
diff --git a/device.h b/device.h
index ef75711..641793a 100644
--- a/device.h
+++ b/device.h
@@ -80,10 +80,13 @@
   // instance.
   const std::string &UniqueName() const;
 
+  std::string GetRpcIdentifier();
+
  protected:
   FRIEND_TEST(DeviceTest, AcquireDHCPConfig);
   FRIEND_TEST(DeviceTest, DestroyIPConfig);
   FRIEND_TEST(DeviceTest, DestroyIPConfigNULL);
+  FRIEND_TEST(DeviceTest, GetProperties);
 
   // If there's an IP configuration in |ipconfig_|, releases the IP address and
   // destroys the configuration instance.
diff --git a/device_dbus_adaptor.h b/device_dbus_adaptor.h
index ee79a76..c6505fe 100644
--- a/device_dbus_adaptor.h
+++ b/device_dbus_adaptor.h
@@ -33,6 +33,7 @@
   virtual ~DeviceDBusAdaptor();
 
   // Implementation of DeviceAdaptorInterface.
+  virtual const std::string &GetRpcIdentifier() { return path(); }
   void UpdateEnabled();
   void EmitBoolChanged(const std::string& name, bool value);
   void EmitUintChanged(const std::string& name, uint32 value);
diff --git a/device_unittest.cc b/device_unittest.cc
index 24a28eb..87eb627 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -16,6 +16,7 @@
 #include "shill/dbus_adaptor.h"
 #include "shill/dhcp_provider.h"
 #include "shill/manager.h"
+#include "shill/mock_adaptors.h"
 #include "shill/mock_control.h"
 #include "shill/mock_device.h"
 #include "shill/mock_glib.h"
@@ -75,6 +76,13 @@
     EXPECT_EQ(props[flimflam::kNameProperty].reader().get_string(),
               string(kDeviceName));
   }
+  {
+    ::DBus::Error dbus_error;
+    DBusAdaptor::GetProperties(device_.get(), &props, &dbus_error);
+    ASSERT_FALSE(props.find(flimflam::kDBusObjectProperty) == props.end());
+    EXPECT_EQ(props[flimflam::kDBusObjectProperty].reader().get_string(),
+              string(DeviceMockAdaptor::kRpcId));
+  }
 }
 
 TEST_F(DeviceTest, Dispatch) {
diff --git a/ethernet_service.cc b/ethernet_service.cc
index 586dc33..88be67d 100644
--- a/ethernet_service.cc
+++ b/ethernet_service.cc
@@ -43,4 +43,8 @@
 
 void EthernetService::Disconnect() { }
 
+std::string EthernetService::GetDeviceRpcId() {
+  return ethernet_->GetRpcIdentifier();
+}
+
 }  // namespace shill
diff --git a/ethernet_service.h b/ethernet_service.h
index df708ae..53cc021 100644
--- a/ethernet_service.h
+++ b/ethernet_service.h
@@ -7,7 +7,6 @@
 
 #include <base/basictypes.h>
 
-#include "shill/ethernet.h"
 #include "shill/refptr_types.h"
 #include "shill/shill_event.h"
 #include "shill/service.h"
@@ -28,6 +27,8 @@
   virtual std::string CalculateState() { return "idle"; }
 
  private:
+  std::string GetDeviceRpcId();
+
   EthernetRefPtr ethernet_;
   const std::string type_;
   DISALLOW_COPY_AND_ASSIGN(EthernetService);
diff --git a/manager_dbus_adaptor.h b/manager_dbus_adaptor.h
index 6a9e0e4..62b8579 100644
--- a/manager_dbus_adaptor.h
+++ b/manager_dbus_adaptor.h
@@ -35,6 +35,7 @@
   virtual ~ManagerDBusAdaptor();
 
   // Implementation of ManagerAdaptorInterface.
+  virtual const std::string &GetRpcIdentifier() { return path(); }
   void UpdateRunning();
   void EmitBoolChanged(const std::string &name, bool value);
   void EmitUintChanged(const std::string &name, uint32 value);
diff --git a/mock_adaptors.cc b/mock_adaptors.cc
new file mode 100644
index 0000000..ae3b97c
--- /dev/null
+++ b/mock_adaptors.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 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 <string>
+
+#include "shill/mock_adaptors.h"
+
+namespace shill {
+
+// static
+const char ManagerMockAdaptor::kRpcId[] = "manager-rpc";
+
+ManagerMockAdaptor::ManagerMockAdaptor() : rpc_id(kRpcId) {}
+
+ManagerMockAdaptor::~ManagerMockAdaptor() {}
+
+const std::string &ManagerMockAdaptor::GetRpcIdentifier() { return rpc_id; }
+
+// static
+const char ServiceMockAdaptor::kRpcId[] = "service-rpc";
+
+ServiceMockAdaptor::ServiceMockAdaptor() : rpc_id(kRpcId) {}
+
+ServiceMockAdaptor::~ServiceMockAdaptor() {}
+
+const std::string &ServiceMockAdaptor::GetRpcIdentifier() { return rpc_id; }
+
+// static
+const char DeviceMockAdaptor::kRpcId[] = "device-rpc";
+
+DeviceMockAdaptor::DeviceMockAdaptor() : rpc_id(kRpcId) {}
+
+DeviceMockAdaptor::~DeviceMockAdaptor() {}
+
+const std::string &DeviceMockAdaptor::GetRpcIdentifier() { return rpc_id; }
+
+}  // namespace shill
diff --git a/mock_adaptors.h b/mock_adaptors.h
index 1993dbb..56a070c 100644
--- a/mock_adaptors.h
+++ b/mock_adaptors.h
@@ -16,8 +16,12 @@
 // These are the functions that a Manager adaptor must support
 class ManagerMockAdaptor : public ManagerAdaptorInterface {
  public:
-  ManagerMockAdaptor() {}
-  virtual ~ManagerMockAdaptor() {}
+  static const char kRpcId[];
+
+  ManagerMockAdaptor();
+  virtual ~ManagerMockAdaptor();
+  virtual const std::string &GetRpcIdentifier();
+
   MOCK_METHOD0(UpdateRunning, void(void));
   MOCK_METHOD2(EmitBoolChanged, void(const std::string&, bool));
   MOCK_METHOD2(EmitUintChanged, void(const std::string&, uint32));
@@ -25,30 +29,47 @@
   MOCK_METHOD2(EmitStringChanged, void(const std::string&, const std::string&));
 
   MOCK_METHOD1(EmitStateChanged, void(const std::string&));
+
+ private:
+  const std::string rpc_id;
 };
 
 // These are the functions that a Service adaptor must support
 class ServiceMockAdaptor : public ServiceAdaptorInterface {
  public:
-  ServiceMockAdaptor() {}
-  virtual ~ServiceMockAdaptor() {}
+  static const char kRpcId[];
+
+  ServiceMockAdaptor();
+  virtual ~ServiceMockAdaptor();
+  virtual const std::string &GetRpcIdentifier();
+
   MOCK_METHOD0(UpdateConnected, void(void));
   MOCK_METHOD2(EmitBoolChanged, void(const std::string&, bool));
   MOCK_METHOD2(EmitUintChanged, void(const std::string&, uint32));
   MOCK_METHOD2(EmitIntChanged, void(const std::string&, int));
   MOCK_METHOD2(EmitStringChanged, void(const std::string&, const std::string&));
+
+ private:
+  const std::string rpc_id;
 };
 
 // These are the functions that a Device adaptor must support
 class DeviceMockAdaptor : public DeviceAdaptorInterface {
  public:
-  DeviceMockAdaptor() {}
-  virtual ~DeviceMockAdaptor() {}
+  static const char kRpcId[];
+
+  DeviceMockAdaptor();
+  virtual ~DeviceMockAdaptor();
+  virtual const std::string &GetRpcIdentifier();
+
   MOCK_METHOD0(UpdateEnabled, void(void));
   MOCK_METHOD2(EmitBoolChanged, void(const std::string&, bool));
   MOCK_METHOD2(EmitUintChanged, void(const std::string&, uint32));
   MOCK_METHOD2(EmitIntChanged, void(const std::string&, int));
   MOCK_METHOD2(EmitStringChanged, void(const std::string&, const std::string&));
+
+ private:
+  const std::string rpc_id;
 };
 
 }  // namespace shill
diff --git a/mock_control.cc b/mock_control.cc
index cd580f8..eaaf2a3 100644
--- a/mock_control.cc
+++ b/mock_control.cc
@@ -7,6 +7,10 @@
 
 namespace shill {
 
+MockControl::MockControl() {}
+
+MockControl::~MockControl() {}
+
 ManagerAdaptorInterface *MockControl::CreateManagerAdaptor(Manager *manager) {
   return new ManagerMockAdaptor();
 }
diff --git a/mock_control.h b/mock_control.h
index 1965916..72edb0a 100644
--- a/mock_control.h
+++ b/mock_control.h
@@ -5,15 +5,26 @@
 #ifndef SHILL_MOCK_CONTROL_
 #define SHILL_MOCK_CONTROL_
 
+#include <base/scoped_ptr.h>
+
 #include "shill/control_interface.h"
 
 namespace shill {
-// This is the Interface for the control channel for Shill.
+// An implementation of the Shill RPC-channel-interface-factory interface that
+// returns mocks.
 class MockControl : public ControlInterface {
  public:
+  MockControl();
+  virtual ~MockControl();
+
+  // Each of these can be called once.  Ownership of the appropriate
+  // interface pointer is given up upon call.
   ManagerAdaptorInterface *CreateManagerAdaptor(Manager *manager);
   ServiceAdaptorInterface *CreateServiceAdaptor(Service *service);
   DeviceAdaptorInterface *CreateDeviceAdaptor(Device *device);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockControl);
 };
 
 }  // namespace shill
diff --git a/mock_service.h b/mock_service.h
index f418b81..6c5dbce 100644
--- a/mock_service.h
+++ b/mock_service.h
@@ -26,7 +26,7 @@
   MOCK_METHOD0(Connect, void(void));
   MOCK_METHOD0(Disconnect, void(void));
   MOCK_METHOD0(CalculateState, std::string(void));
-
+  MOCK_METHOD0(GetDeviceRpcId, std::string(void));
  private:
   DISALLOW_COPY_AND_ASSIGN(MockService);
 };
diff --git a/service.cc b/service.cc
index 247a466..9157cb7 100644
--- a/service.cc
+++ b/service.cc
@@ -44,7 +44,9 @@
   RegisterBool(flimflam::kAutoConnectProperty, &auto_connect_);
   RegisterString(flimflam::kCheckPortalProperty, &check_portal_);
   RegisterConstBool(flimflam::kConnectableProperty, &connectable_);
-  RegisterDerivedString(flimflam::kDeviceProperty, &Service::DeviceRPCID, NULL);
+  RegisterDerivedString(flimflam::kDeviceProperty,
+                        &Service::GetDeviceRpcId,
+                        NULL);
 
   RegisterString(flimflam::kEapIdentityProperty, &eap_.identity);
   RegisterString(flimflam::kEAPEAPProperty, &eap_.eap);
@@ -70,7 +72,7 @@
   RegisterConstString(flimflam::kNameProperty, &name_);
   RegisterInt32(flimflam::kPriorityProperty, &priority_);
   RegisterDerivedString(flimflam::kProfileProperty,
-                        &Service::ProfileRPCID,
+                        &Service::GetProfileRpcId,
                         NULL);
   RegisterString(flimflam::kProxyConfigProperty, &proxy_config_);
   RegisterBool(flimflam::kSaveCredentialsProperty, &save_credentials_);
diff --git a/service.h b/service.h
index 28c5c68..2ad00df 100644
--- a/service.h
+++ b/service.h
@@ -124,10 +124,9 @@
   EventDispatcher *dispatcher_;
 
  private:
-  std::string DeviceRPCID() {
-    return "";  // Will need to call Device to get this.
-  }
-  std::string ProfileRPCID() {
+  virtual std::string GetDeviceRpcId() = 0;
+
+  std::string GetProfileRpcId() {
     return "";  // Will need to call Profile to get this.
   }
 
diff --git a/service_dbus_adaptor.h b/service_dbus_adaptor.h
index a64aa9d..a9e0521 100644
--- a/service_dbus_adaptor.h
+++ b/service_dbus_adaptor.h
@@ -34,6 +34,7 @@
   virtual ~ServiceDBusAdaptor();
 
   // Implementation of ServiceAdaptorInterface.
+  virtual const std::string &GetRpcIdentifier() { return path(); }
   void UpdateConnected();
   void EmitBoolChanged(const std::string& name, bool value);
   void EmitUintChanged(const std::string& name, uint32 value);
diff --git a/service_unittest.cc b/service_unittest.cc
index 5a8014c..1fdfbab 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -16,6 +16,7 @@
 #include "shill/dbus_adaptor.h"
 #include "shill/ethernet_service.h"
 #include "shill/manager.h"
+#include "shill/mock_adaptors.h"
 #include "shill/mock_control.h"
 #include "shill/mock_service.h"
 #include "shill/property_store_unittest.h"
@@ -34,19 +35,29 @@
 
 class ServiceTest : public PropertyStoreTest {
  public:
+  static const char kMockServiceName[];
+  static const char kMockDeviceRpcId[];
+
   ServiceTest()
       : service_(new MockService(&control_interface_,
                                  &dispatcher_,
-                                 "mock-service")) {
+                                 kMockServiceName)) {
   }
 
   virtual ~ServiceTest() {}
 
  protected:
-  ServiceRefPtr service_;
+  scoped_refptr<MockService> service_;
 };
 
+const char ServiceTest::kMockServiceName[] = "mock-service";
+
+const char ServiceTest::kMockDeviceRpcId[] = "mock-device-rpc";
+
 TEST_F(ServiceTest, GetProperties) {
+  EXPECT_CALL(*service_.get(), CalculateState()).WillRepeatedly(Return(""));
+  EXPECT_CALL(*service_.get(), GetDeviceRpcId())
+      .WillRepeatedly(Return(ServiceTest::kMockDeviceRpcId));
   map<string, ::DBus::Variant> props;
   Error error(Error::kInvalidProperty, "");
   {
@@ -84,6 +95,13 @@
     EXPECT_EQ(props[flimflam::kPriorityProperty].reader().get_int32(),
               expected);
   }
+  {
+    ::DBus::Error dbus_error;
+    DBusAdaptor::GetProperties(service_.get(), &props, &dbus_error);
+    ASSERT_FALSE(props.find(flimflam::kDeviceProperty) == props.end());
+    EXPECT_EQ(props[flimflam::kDeviceProperty].reader().get_string(),
+              string(ServiceTest::kMockDeviceRpcId));
+  }
 }
 
 TEST_F(ServiceTest, Dispatch) {
diff --git a/wifi_service.cc b/wifi_service.cc
index 260245f..6f4e959 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -82,4 +82,8 @@
   wifi_->ConnectTo(*this);
 }
 
+string WiFiService::GetDeviceRpcId() {
+  return wifi_->GetRpcIdentifier();
+}
+
 }  // namespace shill
diff --git a/wifi_service.h b/wifi_service.h
index a5a5c58..11ec253 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -37,6 +37,8 @@
  private:
   void RealConnect();
 
+  std::string GetDeviceRpcId();
+
   // Properties
   std::string passphrase_;
   bool need_passphrase_;