shill: implement manager.RequestScan (for WiFi only)

BUG=chromium-os:19831
TEST=unittests, WiFiManager/000_SSID_Length_Limit

note that 000_SSID_Length_Limit does not pass yet,
because we don't do IP configuration yet. but it
does get scan results.

Change-Id: I8993b3c646eda705271d1f7ad7d7341c692ae06b
Reviewed-on: http://gerrit.chromium.org/gerrit/7407
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/adaptor_interfaces.h b/adaptor_interfaces.h
index 595864b..03d804f 100644
--- a/adaptor_interfaces.h
+++ b/adaptor_interfaces.h
@@ -6,6 +6,7 @@
 #define SHILL_ADAPTOR_INTERFACES_
 
 #include <string>
+#include <vector>
 
 #include <base/basictypes.h>
 
@@ -65,6 +66,9 @@
   virtual void EmitIntChanged(const std::string& name, int value) = 0;
   virtual void EmitStringChanged(const std::string& name,
                                  const std::string& value) = 0;
+  virtual void EmitRpcIdentifierArrayChanged(
+      const std::string &name,
+      const std::vector<std::string> &value) = 0;
 
   virtual void EmitStateChanged(const std::string& new_state) = 0;
 };
diff --git a/dbus_adaptor.cc b/dbus_adaptor.cc
index d84ee0a..96672d7 100644
--- a/dbus_adaptor.cc
+++ b/dbus_adaptor.cc
@@ -21,6 +21,8 @@
 namespace shill {
 
 // static
+const char DBusAdaptor::kPathArraySig[] = "ao";
+// static
 const char DBusAdaptor::kStringmapSig[] = "a{ss}";
 // static
 const char DBusAdaptor::kStringmapsSig[] = "aa{ss}";
@@ -166,6 +168,19 @@
 }
 
 // static
+::DBus::Variant DBusAdaptor::PathArrayToVariant(
+    const vector< ::DBus::Path> &value) {
+  ::DBus::MessageIter writer;
+  ::DBus::Variant v;
+
+  // TODO(quiche): figure out why we can't use operator<< without the
+  // temporary variable.
+  writer = v.writer();
+  writer << value;
+  return v;
+}
+
+// static
 ::DBus::Variant DBusAdaptor::StringToVariant(const string &value) {
   ::DBus::Variant v;
   v.writer().append_string(value.c_str());
@@ -245,6 +260,11 @@
 }
 
 // static
+bool DBusAdaptor::IsPathArray(::DBus::Signature signature) {
+  return signature == DBusAdaptor::kPathArraySig;
+}
+
+// static
 bool DBusAdaptor::IsString(::DBus::Signature signature) {
   return signature == ::DBus::type<string>::sig();
 }
diff --git a/dbus_adaptor.h b/dbus_adaptor.h
index 11de9fb..5497445 100644
--- a/dbus_adaptor.h
+++ b/dbus_adaptor.h
@@ -41,6 +41,8 @@
   static ::DBus::Variant Int16ToVariant(int16 value);
   static ::DBus::Variant Int32ToVariant(int32 value);
   static ::DBus::Variant PathToVariant(const ::DBus::Path &value);
+  static ::DBus::Variant PathArrayToVariant(
+      const std::vector< ::DBus::Path> &value);
   static ::DBus::Variant StringToVariant(const std::string &value);
   static ::DBus::Variant StringmapToVariant(const Stringmap &value);
   static ::DBus::Variant StringmapsToVariant(const Stringmaps &value);
@@ -54,6 +56,7 @@
   static bool IsInt16(::DBus::Signature signature);
   static bool IsInt32(::DBus::Signature signature);
   static bool IsPath(::DBus::Signature signature);
+  static bool IsPathArray(::DBus::Signature signature);
   static bool IsString(::DBus::Signature signature);
   static bool IsStringmap(::DBus::Signature signature);
   static bool IsStringmaps(::DBus::Signature signature);
@@ -62,6 +65,7 @@
   static bool IsUint32(::DBus::Signature signature);
 
  private:
+  static const char kPathArraySig[];
   static const char kStringmapSig[];
   static const char kStringmapsSig[];
   static const char kStringsSig[];
diff --git a/dbus_adaptor_unittest.cc b/dbus_adaptor_unittest.cc
index 9c14ab0..eca91f2 100644
--- a/dbus_adaptor_unittest.cc
+++ b/dbus_adaptor_unittest.cc
@@ -41,12 +41,14 @@
         ex_uint32_(2000000),
         ex_int16_(-32768),
         ex_int32_(-65536),
+        ex_path_("/"),
         ex_string_("something"),
         ex_strings_(1, ex_string_),
         bool_v_(DBusAdaptor::BoolToVariant(ex_bool_)),
         byte_v_(DBusAdaptor::ByteToVariant(ex_byte_)),
         int16_v_(DBusAdaptor::Int16ToVariant(ex_int16_)),
         int32_v_(DBusAdaptor::Int32ToVariant(ex_int32_)),
+        path_v_(DBusAdaptor::PathToVariant(ex_path_)),
         string_v_(DBusAdaptor::StringToVariant(ex_string_)),
         strings_v_(DBusAdaptor::StringsToVariant(ex_strings_)),
         uint16_v_(DBusAdaptor::Uint16ToVariant(ex_uint16_)),
@@ -62,6 +64,9 @@
                                  &manager_)) {
     ex_stringmap_[ex_string_] = ex_string_;
     stringmap_v_ = DBusAdaptor::StringmapToVariant(ex_stringmap_);
+
+    ex_patharray_.push_back(ex_path_);
+    patharray_v_ = DBusAdaptor::PathArrayToVariant(ex_patharray_);
   }
 
   virtual ~DBusAdaptorTest() {}
@@ -73,6 +78,8 @@
   uint32 ex_uint32_;
   int16 ex_int16_;
   int32 ex_int32_;
+  ::DBus::Path ex_path_;
+  vector< ::DBus::Path> ex_patharray_;
   string ex_string_;
   map<string, string> ex_stringmap_;
   vector<map<string, string> > ex_stringmaps_;
@@ -82,6 +89,8 @@
   ::DBus::Variant byte_v_;
   ::DBus::Variant int16_v_;
   ::DBus::Variant int32_v_;
+  ::DBus::Variant path_v_;
+  ::DBus::Variant patharray_v_;
   ::DBus::Variant string_v_;
   ::DBus::Variant stringmap_v_;
   ::DBus::Variant stringmaps_v_;
@@ -112,6 +121,10 @@
   EXPECT_EQ(0, PropertyStoreTest::kInt32V.reader().get_int32());
   EXPECT_EQ(ex_int32_, int32_v_.reader().get_int32());
 
+  EXPECT_EQ(ex_path_, path_v_.reader().get_path());
+
+  EXPECT_EQ(ex_path_, patharray_v_.operator vector< ::DBus::Path>()[0]);
+
   EXPECT_EQ(string(""), PropertyStoreTest::kStringV.reader().get_string());
   EXPECT_EQ(ex_string_, string_v_.reader().get_string());
 
@@ -125,6 +138,8 @@
   EXPECT_TRUE(DBusAdaptor::IsByte(byte_v_.signature()));
   EXPECT_TRUE(DBusAdaptor::IsInt16(int16_v_.signature()));
   EXPECT_TRUE(DBusAdaptor::IsInt32(int32_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsPath(path_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsPathArray(patharray_v_.signature()));
   EXPECT_TRUE(DBusAdaptor::IsString(string_v_.signature()));
   EXPECT_TRUE(DBusAdaptor::IsStringmap(stringmap_v_.signature()));
   EXPECT_TRUE(DBusAdaptor::IsStrings(strings_v_.signature()));
diff --git a/device.h b/device.h
index 8d838f2..ed7c413 100644
--- a/device.h
+++ b/device.h
@@ -100,6 +100,7 @@
   FRIEND_TEST(DeviceTest, GetProperties);
   FRIEND_TEST(DeviceTest, Save);
   FRIEND_TEST(DeviceTest, SelectedService);
+  FRIEND_TEST(WiFiMainTest, Connect);
 
   // If there's an IP configuration in |ipconfig_|, releases the IP address and
   // destroys the configuration instance.
diff --git a/manager.cc b/manager.cc
index 1b8810f..19aadbd 100644
--- a/manager.cc
+++ b/manager.cc
@@ -177,6 +177,13 @@
       return;
   }
   services_.push_back(to_manage);
+
+  vector<string> service_paths;
+  for (it = services_.begin(); it != services_.end(); ++it) {
+    service_paths.push_back((*it)->GetRpcIdentifier());
+  }
+  adaptor_->EmitRpcIdentifierArrayChanged(flimflam::kServicesProperty,
+                                          service_paths);
 }
 
 void Manager::DeregisterService(const ServiceConstRefPtr &to_forget) {
@@ -284,4 +291,24 @@
   return ActiveProfile()->GetFriendlyName();
 }
 
+// called via RPC (e.g., from ManagerDBusAdaptor)
+void Manager::RequestScan(const std::string &technology, Error *error) {
+  if (technology == flimflam::kTypeWifi || technology == "") {
+    vector<DeviceRefPtr> wifi_devices;
+    FilterByTechnology(Device::kWifi, &wifi_devices);
+
+    for (vector<DeviceRefPtr>::iterator it = wifi_devices.begin();
+         it != wifi_devices.end();
+         ++it) {
+      (*it)->Scan();
+    }
+  } else {
+    // TODO(quiche): support scanning for other technologies?
+    const string kMessage = "Unrecognized technology " + technology;
+    LOG(ERROR) << kMessage;
+    CHECK(error);
+    error->Populate(Error::kInvalidArguments, kMessage);
+  }
+}
+
 }  // namespace shill
diff --git a/manager.h b/manager.h
index 7c6b6f2..f11aac2 100644
--- a/manager.h
+++ b/manager.h
@@ -58,7 +58,7 @@
   void RegisterDevice(const DeviceRefPtr &to_manage);
   void DeregisterDevice(const DeviceRefPtr &to_forget);
 
-  void RegisterService(const ServiceRefPtr &to_manage);
+  virtual void RegisterService(const ServiceRefPtr &to_manage);
   void DeregisterService(const ServiceConstRefPtr &to_forget);
   virtual void UpdateService(const ServiceConstRefPtr &to_update);
 
@@ -68,6 +68,9 @@
   ServiceRefPtr FindService(const std::string& name);
   std::vector<std::string> EnumerateAvailableServices();
 
+  // called via RPC (e.g., from ManagerDBusAdaptor)
+  void RequestScan(const std::string &technology, Error *error);
+
   virtual DeviceInfo *device_info() { return &device_info_; }
   virtual PropertyStore *store() { return &store_; }
 
diff --git a/manager_dbus_adaptor.cc b/manager_dbus_adaptor.cc
index fa58416..8a9ecb4 100644
--- a/manager_dbus_adaptor.cc
+++ b/manager_dbus_adaptor.cc
@@ -11,6 +11,7 @@
 #include <base/logging.h>
 #include <dbus-c++/dbus.h>
 
+#include "shill/device.h"
 #include "shill/error.h"
 #include "shill/manager.h"
 
@@ -54,6 +55,18 @@
   PropertyChanged(name, DBusAdaptor::StringToVariant(value));
 }
 
+void ManagerDBusAdaptor::EmitRpcIdentifierArrayChanged(
+    const string &name,
+    const vector<string> &value) {
+  vector< ::DBus::Path> paths;
+  vector<string>::const_iterator it;
+  for (it = value.begin(); it != value.end(); ++it) {
+    paths.push_back(*it);
+  }
+
+  PropertyChanged(name, DBusAdaptor::PathArrayToVariant(paths));
+}
+
 void ManagerDBusAdaptor::EmitStateChanged(const string &new_state) {
   StateChanged(new_state);
 }
@@ -101,8 +114,11 @@
 void ManagerDBusAdaptor::PopAnyProfile(::DBus::Error &error) {
 }
 
-void ManagerDBusAdaptor::RequestScan(const string &,
+void ManagerDBusAdaptor::RequestScan(const string &technology,
                                      ::DBus::Error &error) {
+  Error e;
+  manager_->RequestScan(technology, &e);
+  e.ToDBusError(&error);
 }
 
 void ManagerDBusAdaptor::EnableTechnology(const string &,
diff --git a/manager_dbus_adaptor.h b/manager_dbus_adaptor.h
index df0eeb9..bc72273 100644
--- a/manager_dbus_adaptor.h
+++ b/manager_dbus_adaptor.h
@@ -41,6 +41,8 @@
   void EmitUintChanged(const std::string &name, uint32 value);
   void EmitIntChanged(const std::string &name, int value);
   void EmitStringChanged(const std::string &name, const std::string &value);
+  void EmitRpcIdentifierArrayChanged(
+      const std::string &name, const std::vector<std::string> &value);
   void EmitStateChanged(const std::string &new_state);
 
   // Implementation of Manager_adaptor
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 261f061..fb5f78d 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -14,6 +14,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "shill/adaptor_interfaces.h"
+#include "shill/error.h"
+#include "shill/mock_adaptors.h"
 #include "shill/mock_control.h"
 #include "shill/mock_device.h"
 #include "shill/mock_glib.h"
@@ -127,6 +130,9 @@
       .WillRepeatedly(Return(service1_name));
   EXPECT_CALL(*mock_service2.get(), GetRpcIdentifier())
       .WillRepeatedly(Return(service2_name));
+  // TODO(quiche): make this EXPECT_CALL work (crosbug.com/20154)
+  // EXPECT_CALL(*dynamic_cast<ManagerMockAdaptor *>(manager_.adaptor_.get()),
+  //             EmitRpcIdentifierArrayChanged(flimflam::kServicesProperty, _));
 
   manager_.RegisterService(mock_service);
   manager_.RegisterService(mock_service2);
@@ -266,4 +272,25 @@
   }
 }
 
+TEST_F(ManagerTest, RequestScan) {
+  {
+    Error error;
+    manager_.RegisterDevice(mock_device_.get());
+    manager_.RegisterDevice(mock_device2_.get());
+    EXPECT_CALL(*mock_device_, TechnologyIs(Device::kWifi))
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_device_, Scan());
+    EXPECT_CALL(*mock_device2_, TechnologyIs(Device::kWifi))
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*mock_device2_, Scan()).Times(0);
+    manager_.RequestScan(flimflam::kTypeWifi, &error);
+  }
+
+  {
+    Error error;
+    manager_.RequestScan("bogus_device_type", &error);
+    EXPECT_EQ(Error::kInvalidArguments, error.type());
+  }
+}
+
 }  // namespace shill
diff --git a/mock_adaptors.h b/mock_adaptors.h
index 4f3b2e6..30b0901 100644
--- a/mock_adaptors.h
+++ b/mock_adaptors.h
@@ -67,6 +67,8 @@
   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&));
+  MOCK_METHOD2(EmitRpcIdentifierArrayChanged,
+               void(const std::string &, const std::vector<std::string> &));
 
   MOCK_METHOD1(EmitStateChanged, void(const std::string&));
 
diff --git a/mock_device.h b/mock_device.h
index f1205cf..fd73d6c 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -29,6 +29,7 @@
 
   MOCK_METHOD0(Start, void());
   MOCK_METHOD0(Stop, void());
+  MOCK_METHOD0(Scan, void());
   MOCK_CONST_METHOD1(TechnologyIs, bool(const Technology technology));
 
  private:
diff --git a/mock_manager.h b/mock_manager.h
index 21da55e..bc3280e 100644
--- a/mock_manager.h
+++ b/mock_manager.h
@@ -21,6 +21,7 @@
 
   MOCK_METHOD0(device_info, DeviceInfo*(void));
   MOCK_METHOD0(store, PropertyStore*(void));
+  MOCK_METHOD1(RegisterService, void(const ServiceRefPtr &to_manage));
   MOCK_METHOD1(UpdateService, void(const ServiceConstRefPtr &to_update));
 
  private:
diff --git a/wifi.cc b/wifi.cc
index 528c5da..e712ca2 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -37,7 +37,9 @@
 const char WiFi::kSupplicantPropertySSID[]        = "ssid";
 const char WiFi::kSupplicantPropertyNetworkMode[] = "mode";
 const char WiFi::kSupplicantPropertyKeyMode[]     = "key_mgmt";
+const char WiFi::kSupplicantPropertyScanType[]    = "Type";
 const char WiFi::kSupplicantKeyModeNone[] = "NONE";
+const char WiFi::kSupplicantScanTypeActive[] = "active";
 
 // NB: we assume supplicant is already running. [quiche.20110518]
 WiFi::WiFi(ControlInterface *control_interface,
@@ -115,12 +117,7 @@
   // all BSSes (not just new ones since the last scan).
   supplicant_interface_proxy_->FlushBSS(0);
 
-  LOG(INFO) << "initiating Scan.";
-  std::map<string, DBus::Variant> scan_args;
-  scan_args["Type"].writer().append_string("active");
-  // TODO(quiche) indicate scanning in UI
-  supplicant_interface_proxy_->Scan(scan_args);
-  scan_pending_ = true;
+  Scan();
   Device::Start();
 }
 
@@ -131,10 +128,21 @@
   supplicant_process_proxy_.reset();
   endpoint_by_bssid_.clear();
   service_by_private_id_.clear();       // breaks reference cycles
+  // TODO(quiche): unregister services from manager
   Device::Stop();
   // XXX anything else to do?
 }
 
+void WiFi::Scan() {
+  LOG(INFO) << __func__;
+
+  // needs to send a D-Bus message, but may be called from D-Bus
+  // signal handler context (via Manager::RequestScan). so defer work
+  // to event loop.
+  dispatcher()->PostTask(
+      task_factory_.NewRunnableMethod(&WiFi::ScanTask));
+}
+
 bool WiFi::TechnologyIs(const Device::Technology type) const {
   return type == Device::kWifi;
 }
@@ -165,19 +173,21 @@
       task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
 }
 
-void WiFi::ConnectTo(const WiFiService &service) {
+void WiFi::ConnectTo(WiFiService *service) {
   std::map<string, DBus::Variant> add_network_args;
   DBus::MessageIter writer;
   DBus::Path network_path;
 
+  // TODO(quiche): if already connected, disconnect
+
   add_network_args[kSupplicantPropertyNetworkMode].writer().
-      append_uint32(WiFiEndpoint::ModeStringToUint(service.mode()));
+      append_uint32(WiFiEndpoint::ModeStringToUint(service->mode()));
   add_network_args[kSupplicantPropertyKeyMode].writer().
-      append_string(service.key_management().c_str());
+      append_string(service->key_management().c_str());
   // TODO(quiche): figure out why we can't use operator<< without the
   // temporary variable.
   writer = add_network_args[kSupplicantPropertySSID].writer();
-  writer << service.ssid();
+  writer << service->ssid();
   // TODO(quiche): set scan_ssid=1, like flimflam does?
 
   network_path =
@@ -219,13 +229,23 @@
                           kSupplicantKeyModeNone));
       services()->push_back(service);
       service_by_private_id_[service_id_private] = service;
+      manager()->RegisterService(service);
 
       LOG(INFO) << "new service " << service->GetRpcIdentifier();
     }
   }
 
-  // TODO(quiche): register new services with manager
   // TODO(quiche): unregister removed services from manager
 }
 
+void WiFi::ScanTask() {
+  VLOG(2) << "WiFi " << link_name() << " scan requested.";
+  std::map<string, DBus::Variant> scan_args;
+  scan_args[kSupplicantPropertyScanType].writer().
+      append_string(kSupplicantScanTypeActive);
+  // TODO(quiche) indicate scanning in UI
+  supplicant_interface_proxy_->Scan(scan_args);
+  scan_pending_ = true;
+}
+
 }  // namespace shill
diff --git a/wifi.h b/wifi.h
index 8a35e95..c58aa8c 100644
--- a/wifi.h
+++ b/wifi.h
@@ -33,6 +33,7 @@
   virtual ~WiFi();
   virtual void Start();
   virtual void Stop();
+  virtual void Scan();
   virtual bool TechnologyIs(const Technology type) const;
 
   // called by SupplicantInterfaceProxy, in response to events from
@@ -42,7 +43,7 @@
   void ScanDone();
 
   // called by WiFiService
-  void ConnectTo(const WiFiService &service);
+  void ConnectTo(WiFiService *service);
 
  private:
   typedef std::map<const std::string, WiFiEndpointRefPtr> EndpointMap;
@@ -55,9 +56,12 @@
   static const char kSupplicantPropertySSID[];
   static const char kSupplicantPropertyNetworkMode[];
   static const char kSupplicantPropertyKeyMode[];
+  static const char kSupplicantPropertyScanType[];
   static const char kSupplicantKeyModeNone[];
+  static const char kSupplicantScanTypeActive[];
 
   void ScanDoneTask();
+  void ScanTask();
 
   ScopedRunnableMethodFactory<WiFi> task_factory_;
   scoped_ptr<SupplicantProcessProxyInterface> supplicant_process_proxy_;
diff --git a/wifi_service.cc b/wifi_service.cc
index bdfa6e7..4127b55 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -21,9 +21,6 @@
 
 namespace shill {
 
-// static
-const char WiFiService::kServiceType[] = "wifi";
-
 WiFiService::WiFiService(ControlInterface *control_interface,
                          EventDispatcher *dispatcher,
                          Manager *manager,
@@ -33,6 +30,7 @@
                          const std::string &key_management)
     : Service(control_interface, dispatcher, manager),
       security_(flimflam::kSecurityNone),
+      type_(flimflam::kTypeWifi),
       mode_(mode),
       task_factory_(this),
       wifi_(device),
@@ -51,7 +49,13 @@
   store->RegisterConstBool(flimflam::kWifiHiddenSsid, &hidden_ssid_);
   store->RegisterConstUint16(flimflam::kWifiFrequency, &frequency_);
   store->RegisterConstUint16(flimflam::kWifiPhyMode, &physical_mode_);
-  store->RegisterConstUint16(flimflam::kWifiHexSsid, &hex_ssid_);
+  store->RegisterConstString(flimflam::kWifiHexSsid, &hex_ssid_);
+
+  hex_ssid_ = base::HexEncode(&(*ssid_.begin()), ssid_.size());
+  // TODO(quiche): set based on security properties
+  need_passphrase_ = false;
+  // TODO(quiche): figure out when to set true
+  hidden_ssid_ = false;
 }
 
 WiFiService::~WiFiService() {
@@ -73,11 +77,10 @@
 }
 
 string WiFiService::GetStorageIdentifier(const std::string &mac) {
-  string ssid_hex = base::HexEncode(&(*ssid_.begin()), ssid_.size());
   return StringToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s",
-                                               kServiceType,
+                                               flimflam::kTypeWifi,
                                                mac.c_str(),
-                                               ssid_hex.c_str(),
+                                               hex_ssid_.c_str(),
                                                mode_.c_str(),
                                                security_.c_str()));
 }
@@ -94,8 +97,9 @@
   return ssid_;
 }
 
+// private methods
 void WiFiService::ConnectTask() {
-  wifi_->ConnectTo(*this);
+  wifi_->ConnectTo(this);
 }
 
 string WiFiService::GetDeviceRpcId() {
diff --git a/wifi_service.h b/wifi_service.h
index fe301dc..9f654ab 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -45,8 +45,6 @@
   virtual std::string CalculateState() { return "idle"; }
 
  private:
-  static const char kServiceType[];
-
   void ConnectTask();
 
   std::string GetDeviceRpcId();
@@ -64,7 +62,7 @@
   bool hidden_ssid_;
   uint16 frequency_;
   uint16 physical_mode_;
-  uint16 hex_ssid_;
+  std::string hex_ssid_;
 
   ScopedRunnableMethodFactory<WiFiService> task_factory_;
   WiFiRefPtr wifi_;
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 0d71cbb..2d5cd9f 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -27,6 +27,7 @@
 #include "shill/proxy_factory.h"
 #include "shill/wifi_endpoint.h"
 #include "shill/wifi.h"
+#include "shill/wifi_service.h"
 
 using std::map;
 using std::string;
@@ -97,7 +98,7 @@
   WiFiMainTest()
       : manager_(&control_interface_, NULL, NULL),
         wifi_(new WiFi(&control_interface_,
-                       NULL,
+                       &dispatcher_,
                        &manager_,
                        kDeviceName,
                        kDeviceAddress,
@@ -152,7 +153,7 @@
   SupplicantInterfaceProxyInterface *GetSupplicantInterfaceProxy() {
     return wifi_->supplicant_interface_proxy_.get();
   }
-  void InitiateConnect(const WiFiService &service) {
+  void InitiateConnect(WiFiService *service) {
     wifi_->ConnectTo(service);
   }
   void ReportBSS(const ::DBus::Path &bss_path,
@@ -169,6 +170,14 @@
   void StopWiFi() {
     wifi_->Stop();
   }
+  MockManager *manager() {
+    return &manager_;
+  }
+  const WiFiConstRefPtr wifi() const {
+    return wifi_;
+  }
+
+  EventDispatcher dispatcher_;
 
  private:
   NiceMockControl control_interface_;
@@ -239,6 +248,7 @@
               "test threw fi.w1.wpa_supplicant1.InterfaceUnknown")));
   EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
   StartWiFi();
+  dispatcher_.DispatchPendingEvents();
 }
 
 TEST_F(WiFiMainTest, Restart) {
@@ -251,6 +261,7 @@
   EXPECT_CALL(*supplicant_process_proxy_, GetInterface(_));
   EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
   StartWiFi();
+  dispatcher_.DispatchPendingEvents();
 }
 
 TEST_F(WiFiMainTest, StartClearsState) {
@@ -295,6 +306,8 @@
       "bss1", "ssid1", "00:00:00:00:00:01", 1, kNetworkModeInfrastructure);
   ReportBSS(
       "bss2", "ssid2", "00:00:00:00:00:02", 2, kNetworkModeInfrastructure);
+  EXPECT_CALL(*manager(), RegisterService(_))
+      .Times(3);
   ReportScanDone();
   EXPECT_EQ(3, GetServiceMap().size());
 }
@@ -310,11 +323,12 @@
   {
     InSequence s;
     DBus::Path fake_path("/fake/path");
+    WiFiService *service(GetServiceMap().begin()->second);
 
     EXPECT_CALL(supplicant_interface_proxy, AddNetwork(_))
         .WillOnce(Return(fake_path));
     EXPECT_CALL(supplicant_interface_proxy, SelectNetwork(fake_path));
-    InitiateConnect(*(GetServiceMap().begin()->second));
+    InitiateConnect(service);
   }
 }