shill: WiFiProvider: Move Service vector to WiFiProvider

Remove the services_ vector from the WiFi Device and move it
to the WiFiProvider.  Also remove the WiFi device initializer
from the WiFiService, so it doesn't have an early or permanent
binding to a particular device.  This allows WiFi services to
be loaded immediately as a profile loads, so that operations
that operate on services (like Manager::ConfigureService
and Manager::FindMatchingService) do not need to use a separate
API to find and modify services that are not visible but have
stored configuration associated with them.

This also allows Chrome a somewhat more stabilized service
path to remembered services as they appear and disappear from
view.  Another advantage is that this completely regularizes
the relationship between the presence of the service in the
provider's services_ list and its registration with the
manager.

In order to perform late-binding to a WiFi device, we provide
two methods for WiFi services to find a device to call
WiFi::ConnectTo on when the time comes: Firstly, visible
WiFi services (ones with endpoints) can select the device
associated with the most "promising" endpoint.  In the case
where we try to connect to a hidden WiFi service before
endpoints appear for it, there is a new method for selecting
a WiFi device from the Manager.  In both of these cases only
one WiFi device is selected for the connect request, so this
method is no worse than before in the unlikely case where
there are two WiFi devices, except for the fact that now
there won't be duplicate WiFi services registered in the
Manager.

CQ-DEPEND=Ic8af4999b25503c3b002504edd12405dc91cc824
BUG=chromium-os:38017
TEST=Unit tests; manual operation; manager unit tests, WiFiManager
autotests (profile tests failing due to crosbug.com/35374)

Change-Id: I904df8a983ba6e7e76e20159622c652675eb6a7d
Reviewed-on: https://gerrit.chromium.org/gerrit/41664
Commit-Queue: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/Makefile b/Makefile
index 5574a94..feecb10 100644
--- a/Makefile
+++ b/Makefile
@@ -430,6 +430,7 @@
 	mock_vpn_provider.o \
 	mock_vpn_service.o \
 	mock_wifi.o \
+	mock_wifi_provider.o \
 	mock_wifi_service.o \
 	mock_wimax.o \
 	mock_wimax_device_proxy.o \
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
index b8afd48..834a61a 100644
--- a/doc/manager-api.txt
+++ b/doc/manager-api.txt
@@ -408,10 +408,10 @@
 
 		array{object} Services [readonly]
 
-			List of service object paths. The list is sorted
-			internally to have the service with the default
-			route always first and then the favorite services
-			followed by scan results.
+			List of service object paths that are visible. The
+			list is sorted internally to have the service with
+			the default route always first and then the favorite
+			services followed by scan results.
 
 			This list represents the available services for the
 			current selected profile. If the profile gets changed
@@ -422,6 +422,18 @@
 			applications only dealing with the current active
 			profile.
 
+		array{object} ServiceCompleteList [readonly]
+
+			Complete list of service object paths, including those
+			that are not currently visible.  For example, WiFi
+			services that are stored in a loaded profile but
+			cannot currently be connected are presented in this
+			list.
+
+			The services are listed in the same service sorting
+			order as the "Services" property above.  Change
+			events for this property are not emitted.
+
 		array{object} ServiceWatchList [readonly]
 
 			List of service object paths that are in a
@@ -434,7 +446,7 @@
 
 			The services are listed in the same service sorting
 			order as the "Services" property above, and a change
-			event for this service is emitted every time the
+			event for this property is emitted every time the
 			Service list is reordered, even if this list has not
 			changed.
 
diff --git a/doc/service-api.txt b/doc/service-api.txt
index 4c79bf4..3d5d4a0 100644
--- a/doc/service-api.txt
+++ b/doc/service-api.txt
@@ -86,6 +86,7 @@
 			connection attempt via the Connect method.
 
 			Possible Errors: [service].Error.InvalidArguments
+			                 [service].Error.OperationFailed
 
 		void Remove()
 
diff --git a/manager.cc b/manager.cc
index 98a22c5..4c5ab31 100644
--- a/manager.cc
+++ b/manager.cc
@@ -43,6 +43,7 @@
 #include "shill/service_sorter.h"
 #include "shill/vpn_service.h"
 #include "shill/wifi.h"
+#include "shill/wifi_provider.h"
 #include "shill/wifi_service.h"
 #include "shill/wimax_service.h"
 
@@ -89,6 +90,8 @@
       device_info_(control_interface, dispatcher, metrics, this),
       modem_info_(control_interface, dispatcher, metrics, this, glib),
       vpn_provider_(control_interface, dispatcher, metrics, this),
+      wifi_provider_(
+          new WiFiProvider(control_interface, dispatcher, metrics, this)),
       wimax_provider_(control_interface, dispatcher, metrics, this),
       resolver_(Resolver::GetInstance()),
       running_(false),
@@ -144,6 +147,8 @@
                             NULL);
   HelpRegisterConstDerivedRpcIdentifiers(flimflam::kServicesProperty,
                                          &Manager::EnumerateAvailableServices);
+  HelpRegisterConstDerivedRpcIdentifiers(shill::kServiceCompleteListProperty,
+                                         &Manager::EnumerateCompleteServices);
   HelpRegisterConstDerivedRpcIdentifiers(flimflam::kServiceWatchListProperty,
                                          &Manager::EnumerateWatchedServices);
   store_.RegisterString(shill::kShortDNSTimeoutTechnologiesProperty,
@@ -199,6 +204,7 @@
   device_info_.Start();
   modem_info_.Start();
   vpn_provider_.Start();
+  wifi_provider_->Start();
   wimax_provider_.Start();
 }
 
@@ -230,6 +236,7 @@
 
   adaptor_->UpdateRunning();
   wimax_provider_.Stop();
+  wifi_provider_->Stop();
   vpn_provider_.Stop();
   modem_info_.Stop();
   device_info_.Stop();
@@ -378,8 +385,8 @@
     profile->ConfigureService(*it);
   }
 
-  // Shop the Profile contents around to Devices which can create
-  // non-visible services.
+  // Shop the Profile contents around to Devices which may have configuration
+  // stored in these profiles.
   for (vector<DeviceRefPtr>::iterator it = devices_.begin();
        it != devices_.end(); ++it) {
     profile->ConfigureDevice(*it);
@@ -388,6 +395,7 @@
   // Offer the Profile contents to the service/device providers which will
   // create new services if necessary.
   vpn_provider_.CreateServicesFromProfile(profile);
+  wifi_provider_->CreateServicesFromProfile(profile);
   wimax_provider_.CreateServicesFromProfile(profile);
 
   *path = profile->GetRpcIdentifier();
@@ -602,11 +610,25 @@
   return IsTechnologyInList(props_.link_monitor_technologies, technology);
 }
 
-bool Manager::IsDefaultProfile(const StoreInterface *storage) const {
-  if (profiles_.empty()) {
-    return false;
+bool Manager::IsDefaultProfile(StoreInterface *storage) {
+  return profiles_.empty() || storage == profiles_.front()->GetConstStorage();
+}
+
+void Manager::OnProfileStorageInitialized(StoreInterface *storage) {
+  wifi_provider_->FixupServiceEntries(storage, IsDefaultProfile(storage));
+}
+
+DeviceRefPtr Manager::GetEnabledDeviceWithTechnology(
+    Technology::Identifier technology) const {
+  vector<DeviceRefPtr> devices;
+  FilterByTechnology(technology, &devices);
+  for (vector<DeviceRefPtr>::const_iterator it = devices.begin();
+       it != devices.end(); ++it) {
+    if ((*it)->enabled()) {
+      return *it;
+    }
   }
-  return storage == profiles_.front()->GetConstStorage();
+  return NULL;
 }
 
 const ProfileRefPtr &Manager::ActiveProfile() const {
@@ -1024,9 +1046,9 @@
 }
 
 void Manager::FilterByTechnology(Technology::Identifier tech,
-                                 vector<DeviceRefPtr> *found) {
+                                 vector<DeviceRefPtr> *found) const {
   CHECK(found);
-  vector<DeviceRefPtr>::iterator it;
+  vector<DeviceRefPtr>::const_iterator it;
   for (it = devices_.begin(); it != devices_.end(); ++it) {
     if ((*it)->technology() == tech)
       found->push_back(*it);
@@ -1224,6 +1246,15 @@
   return vector<string>(unique_technologies.begin(), unique_technologies.end());
 }
 
+bool Manager::IsTechnologyConnected(Technology::Identifier technology) const {
+  for (vector<DeviceRefPtr>::const_iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    if ((*it)->technology() == technology && (*it)->IsConnected())
+      return true;
+  }
+  return false;
+}
+
 string Manager::DefaultTechnology(Error */*error*/) {
   return (!services_.empty() && services_[0]->IsConnected()) ?
       services_[0]->GetTechnologyString() : "";
@@ -1276,6 +1307,16 @@
   return service_rpc_ids;
 }
 
+RpcIdentifiers Manager::EnumerateCompleteServices(Error */*error*/) {
+  vector<string> service_rpc_ids;
+  for (vector<ServiceRefPtr>::const_iterator it = services_.begin();
+       it != services_.end();
+       ++it) {
+    service_rpc_ids.push_back((*it)->GetRpcIdentifier());
+  }
+  return service_rpc_ids;
+}
+
 RpcIdentifiers Manager::EnumerateWatchedServices(Error */*error*/) {
   vector<string> service_rpc_ids;
   for (vector<ServiceRefPtr>::const_iterator it = services_.begin();
@@ -1358,7 +1399,7 @@
   }
   if (type == flimflam::kTypeWifi) {
     SLOG(Manager, 2) << __func__ << ": getting WiFi Service";
-    return GetWifiService(args, error);
+    return wifi_provider_->GetService(args, error);
   }
   if (type == flimflam::kTypeWimax) {
     SLOG(Manager, 2) << __func__ << ": getting WiMAX Service";
@@ -1369,20 +1410,6 @@
   return NULL;
 }
 
-WiFiServiceRefPtr Manager::GetWifiService(const KeyValueStore &args,
-                                          Error *error) {
-  vector<DeviceRefPtr> wifi_devices;
-  FilterByTechnology(Technology::kWifi, &wifi_devices);
-  if (wifi_devices.empty()) {
-    Error::PopulateAndLog(error, Error::kInvalidArguments, kErrorNoDevice);
-    return NULL;
-  } else {
-    WiFi *wifi = dynamic_cast<WiFi *>(wifi_devices.front().get());
-    CHECK(wifi);
-    return wifi->GetService(args, error);
-  }
-}
-
 // called via RPC (e.g., from ManagerDBusAdaptor)
 ServiceRefPtr Manager::ConfigureService(const KeyValueStore &args,
                                         Error *error) {
diff --git a/manager.h b/manager.h
index 5d9a32f..c7c2609 100644
--- a/manager.h
+++ b/manager.h
@@ -41,6 +41,7 @@
 class ManagerAdaptorInterface;
 class Resolver;
 class StoreInterface;
+class WiFiProvider;
 
 class Manager : public base::SupportsWeakPtr<Manager> {
  public:
@@ -113,11 +114,14 @@
   virtual void UpdateDevice(const DeviceRefPtr &to_update);
 
   void FilterByTechnology(Technology::Identifier tech,
-                          std::vector<DeviceRefPtr> *found);
+                          std::vector<DeviceRefPtr> *found) const;
 
   ServiceRefPtr FindService(const std::string& name);
   std::vector<std::string> EnumerateAvailableServices(Error *error);
 
+  // Return the complete list of services, including those that are not visible.
+  std::vector<std::string> EnumerateCompleteServices(Error *error);
+
   // called via RPC (e.g., from ManagerDBusAdaptor)
   ServiceRefPtr GetService(const KeyValueStore &args, Error *error);
   ServiceRefPtr ConfigureService(const KeyValueStore &args, Error *error);
@@ -131,7 +135,7 @@
   // |service|.
   virtual void RecheckPortalOnService(const ServiceRefPtr &service);
 
-  void RequestScan(const std::string &technology, Error *error);
+  virtual void RequestScan(const std::string &technology, Error *error);
   std::string GetTechnologyOrder();
   void SetTechnologyOrder(const std::string &order, Error *error);
   // Set up the profile list starting with a default profile along with
@@ -190,6 +194,9 @@
   // Return whether a service belongs to the ephemeral profile.
   virtual bool IsServiceEphemeral(const ServiceConstRefPtr &service) const;
 
+  // Return whether a Technology has any connected Services.
+  virtual bool IsTechnologyConnected(Technology::Identifier technology) const;
+
   // Return whether a technology is enabled for using short DNS timeouts.
   bool IsTechnologyShortDNSTimeoutEnabled(Technology::Identifier tech) const;
 
@@ -197,8 +204,12 @@
   virtual bool IsTechnologyLinkMonitorEnabled(
       Technology::Identifier technology) const;
 
-  // Return whether |storage| is for the default profile.
-  virtual bool IsDefaultProfile(const StoreInterface *storage) const;
+  // Called by Profile when a |storage| completes initialization.
+  void OnProfileStorageInitialized(StoreInterface *storage);
+
+  // Return a Device with technology |technology| in the enabled state.
+  DeviceRefPtr GetEnabledDeviceWithTechnology(
+      Technology::Identifier technology) const;
 
   // Returns true if at least one connection exists, and false if there's no
   // connected service.
@@ -217,6 +228,7 @@
   virtual ModemInfo *modem_info() { return &modem_info_; }
   PowerManager *power_manager() const { return power_manager_.get(); }
   virtual VPNProvider *vpn_provider() { return &vpn_provider_; }
+  virtual WiFiProvider *wifi_provider() { return wifi_provider_.get(); }
   virtual WiMaxProvider *wimax_provider() { return &wimax_provider_; }
   PropertyStore *mutable_store() { return &store_; }
   virtual const PropertyStore &store() const { return store_; }
@@ -238,9 +250,6 @@
   // ephemeral, it is moved to the current profile.
   void SaveServiceToProfile(const ServiceRefPtr &to_update);
 
-  // Configure the device with profile data from all current profiles.
-  virtual void LoadDeviceFromProfiles(const DeviceRefPtr &device);
-
   // Adds a closure to be executed when ChromeOS suspends or shill terminates.
   // |name| should be unique; otherwise, a previous closure by the same name
   // will be replaced.  |start| will be called when RunTerminationActions() is
@@ -292,6 +301,7 @@
   FRIEND_TEST(ManagerTest, EnableTechnology);
   FRIEND_TEST(ManagerTest, EnumerateProfiles);
   FRIEND_TEST(ManagerTest, HandleProfileEntryDeletionWithUnload);
+  FRIEND_TEST(ManagerTest, IsDefaultProfile);
   FRIEND_TEST(ManagerTest, LinkMonitorEnabled);
   FRIEND_TEST(ManagerTest, NotifyDefaultServiceChanged);
   FRIEND_TEST(ManagerTest, PopProfileWithUnload);
@@ -309,8 +319,6 @@
 
   static const char kPowerManagerKey[];
 
-  WiFiServiceRefPtr GetWifiService(const KeyValueStore &args, Error *error);
-
   void AutoConnect();
   std::vector<std::string> AvailableTechnologies(Error *error);
   std::vector<std::string> ConnectedTechnologies(Error *error);
@@ -331,6 +339,7 @@
   void EmitDefaultService();
   bool IsTechnologyInList(const std::string &technology_list,
                           Technology::Identifier tech) const;
+  bool IsDefaultProfile(StoreInterface *storage);
   void EmitDeviceProperties();
 
   // Unload a service while iterating through |services_|.  Returns true if
@@ -342,6 +351,9 @@
   // Load Manager default properties from |profile|.
   bool LoadProperties(const scoped_refptr<DefaultProfile> &profile);
 
+  // Configure the device with profile data from all current profiles.
+  void LoadDeviceFromProfiles(const DeviceRefPtr &device);
+
   void HelpRegisterConstDerivedRpcIdentifier(
       const std::string &name,
       RpcIdentifier(Manager::*get)(Error *));
@@ -392,6 +404,7 @@
   DeviceInfo device_info_;
   ModemInfo modem_info_;
   VPNProvider vpn_provider_;
+  scoped_ptr<WiFiProvider> wifi_provider_;
   WiMaxProvider wimax_provider_;
   // Hold pointer to singleton Resolver instance for testing purposes.
   Resolver *resolver_;
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 0c8b981..f1d000d 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -36,7 +36,7 @@
 #include "shill/mock_resolver.h"
 #include "shill/mock_service.h"
 #include "shill/mock_store.h"
-#include "shill/mock_wifi.h"
+#include "shill/mock_wifi_provider.h"
 #include "shill/mock_wifi_service.h"
 #include "shill/property_store_unittest.h"
 #include "shill/proxy_factory.h"
@@ -70,19 +70,13 @@
  public:
   ManagerTest()
       : power_manager_(new MockPowerManager(NULL, &proxy_factory_)),
-        mock_wifi_(new NiceMock<MockWiFi>(control_interface(),
-                                          dispatcher(),
-                                          metrics(),
-                                          manager(),
-                                          "wifi0",
-                                          "addr4",
-                                          4)),
         device_info_(new NiceMock<MockDeviceInfo>(
             control_interface(),
             reinterpret_cast<EventDispatcher*>(NULL),
             reinterpret_cast<Metrics*>(NULL),
             reinterpret_cast<Manager*>(NULL))),
-        manager_adaptor_(new NiceMock<ManagerMockAdaptor>()) {
+        manager_adaptor_(new NiceMock<ManagerMockAdaptor>()),
+        wifi_provider_(new NiceMock<MockWiFiProvider>()) {
     mock_devices_.push_back(new NiceMock<MockDevice>(control_interface(),
                                                      dispatcher(),
                                                      metrics(),
@@ -117,6 +111,10 @@
     // Replace the manager's adaptor with a quieter one, and one
     // we can do EXPECT*() against.  Passes ownership.
     manager()->adaptor_.reset(manager_adaptor_);
+
+    // Replace the manager's WiFi provider with our mock.  Passes
+    // ownership.
+    manager()->wifi_provider_.reset(wifi_provider_);
   }
   virtual ~ManagerTest() {}
 
@@ -320,12 +318,13 @@
 
   TestProxyFactory proxy_factory_;
   scoped_ptr<MockPowerManager> power_manager_;
-  scoped_refptr<MockWiFi> mock_wifi_;
   vector<scoped_refptr<MockDevice> > mock_devices_;
   scoped_ptr<MockDeviceInfo> device_info_;
 
-  // This pointer is owned by the manager, and only tracked here for EXPECT*()
+  // These pointers are owned by the manager, and only tracked here for
+  // EXPECT*()
   ManagerMockAdaptor *manager_adaptor_;
+  MockWiFiProvider *wifi_provider_;
 };
 
 const char ManagerTest::TerminationActionTest::kActionName[] = "action";
@@ -1377,22 +1376,12 @@
   EXPECT_EQ("service type is unsupported", e.message());
 }
 
-TEST_F(ManagerTest, GetServiceNoWifiDevice) {
-  KeyValueStore args;
-  Error e;
-  args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
-  manager()->GetService(args, &e);
-  EXPECT_EQ(Error::kInvalidArguments, e.type());
-  EXPECT_EQ("no wifi devices available", e.message());
-}
-
 TEST_F(ManagerTest, GetServiceWifi) {
   KeyValueStore args;
   Error e;
   WiFiServiceRefPtr wifi_service;
   args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
-  manager()->RegisterDevice(mock_wifi_);
-  EXPECT_CALL(*mock_wifi_, GetService(_, _))
+  EXPECT_CALL(*wifi_provider_, GetService(_, _))
       .WillRepeatedly(Return(wifi_service));
   manager()->GetService(args, &e);
   EXPECT_TRUE(e.IsSuccess());
@@ -1498,7 +1487,7 @@
                                     dispatcher(),
                                     metrics(),
                                     manager(),
-                                    mock_wifi_,
+                                    wifi_provider_,
                                     ssid,
                                     "",
                                     "",
@@ -1507,17 +1496,7 @@
   manager()->RegisterService(service);
   service->set_profile(GetEphemeralProfile(manager()));
 
-  // A separate MockWiFi from mock_wifi_ is used in the Manager since using
-  // the same device as that used above causes a refcounting loop.
-  scoped_refptr<MockWiFi> wifi(new NiceMock<MockWiFi>(control_interface(),
-                                                      dispatcher(),
-                                                      metrics(),
-                                                      manager(),
-                                                      "wifi1",
-                                                      "addr5",
-                                                      5));
-  manager()->RegisterDevice(wifi);
-  EXPECT_CALL(*wifi, GetService(_, _))
+  EXPECT_CALL(*wifi_provider_, GetService(_, _))
       .WillOnce(Return(service));
   EXPECT_CALL(*profile, UpdateService(ServiceRefPtr(service.get())))
       .WillOnce(Return(true));
@@ -1557,7 +1536,7 @@
                                     dispatcher(),
                                     metrics(),
                                     manager(),
-                                    mock_wifi_,
+                                    wifi_provider_,
                                     ssid,
                                     "",
                                     "",
@@ -1566,17 +1545,7 @@
   manager()->RegisterService(service);
   service->set_profile(profile1);
 
-  // A separate MockWiFi from mock_wifi_ is used in the Manager since using
-  // the same device as that used above causes a refcounting loop.
-  scoped_refptr<MockWiFi> wifi(new NiceMock<MockWiFi>(control_interface(),
-                                                      dispatcher(),
-                                                      metrics(),
-                                                      manager(),
-                                                      "wifi1",
-                                                      "addr5",
-                                                      5));
-  manager()->RegisterDevice(wifi);
-  EXPECT_CALL(*wifi, GetService(_, _))
+  EXPECT_CALL(*wifi_provider_, GetService(_, _))
       .WillOnce(Return(service));
   EXPECT_CALL(*profile0, LoadService(ServiceRefPtr(service.get())))
       .WillOnce(Return(true));
@@ -1616,7 +1585,7 @@
                                     dispatcher(),
                                     metrics(),
                                     manager(),
-                                    mock_wifi_,
+                                    wifi_provider_,
                                     ssid,
                                     "",
                                     "",
@@ -1625,17 +1594,7 @@
   manager()->RegisterService(service);
   service->set_profile(profile0);
 
-  // A separate MockWiFi from mock_wifi_ is used in the Manager since using
-  // the same device as that used above causes a refcounting loop.
-  scoped_refptr<MockWiFi> wifi(new NiceMock<MockWiFi>(control_interface(),
-                                                      dispatcher(),
-                                                      metrics(),
-                                                      manager(),
-                                                      "wifi1",
-                                                      "addr5",
-                                                      5));
-  manager()->RegisterDevice(wifi);
-  EXPECT_CALL(*wifi, GetService(_, _))
+  EXPECT_CALL(*wifi_provider_, GetService(_, _))
       .WillOnce(Return(service));
   EXPECT_CALL(*profile0, LoadService(ServiceRefPtr(service.get())))
       .Times(0);
@@ -1678,7 +1637,7 @@
                                     dispatcher(),
                                     metrics(),
                                     manager(),
-                                    mock_wifi_,
+                                    wifi_provider_,
                                     ssid,
                                     "",
                                     "",
@@ -1686,17 +1645,7 @@
 
   service->set_profile(profile1);
 
-  // A separate MockWiFi from mock_wifi_ is used in the Manager since using
-  // the same device as that used above causes a refcounting loop.
-  scoped_refptr<MockWiFi> wifi(new NiceMock<MockWiFi>(control_interface(),
-                                                      dispatcher(),
-                                                      metrics(),
-                                                      manager(),
-                                                      "wifi1",
-                                                      "addr5",
-                                                      5));
-  manager()->RegisterDevice(wifi);
-  EXPECT_CALL(*wifi, GetService(_, _))
+  EXPECT_CALL(*wifi_provider_, GetService(_, _))
       .WillOnce(Return(service));
   EXPECT_CALL(*profile0, UpdateService(ServiceRefPtr(service.get())))
       .WillOnce(Return(true));
@@ -2739,9 +2688,9 @@
 }
 
 TEST_F(ManagerTest, IsDefaultProfile) {
-  EXPECT_FALSE(manager()->IsDefaultProfile(NULL));
+  EXPECT_TRUE(manager()->IsDefaultProfile(NULL));
   scoped_ptr<MockStore> store0(new MockStore);
-  EXPECT_FALSE(manager()->IsDefaultProfile(store0.get()));
+  EXPECT_TRUE(manager()->IsDefaultProfile(store0.get()));
   scoped_refptr<MockProfile> profile(
       new MockProfile(control_interface(), manager(), ""));
   EXPECT_CALL(*profile, GetConstStorage()).WillRepeatedly(Return(store0.get()));
diff --git a/metrics_unittest.cc b/metrics_unittest.cc
index 9f44e82..cb514b0 100644
--- a/metrics_unittest.cc
+++ b/metrics_unittest.cc
@@ -41,18 +41,11 @@
                                  &dispatcher_,
                                  &metrics_,
                                  &manager_)),
-        wifi_(new WiFi(&control_interface_,
-                       &dispatcher_,
-                       &metrics_,
-                       &manager_,
-                       "wlan0",
-                       "000102030405",
-                       0)),
         wifi_service_(new MockWiFiService(&control_interface_,
                                           &dispatcher_,
                                           &metrics_,
                                           &manager_,
-                                          wifi_,
+                                          manager_.wifi_provider(),
                                           ssid_,
                                           flimflam::kModeManaged,
                                           flimflam::kSecurityNone,
@@ -95,7 +88,6 @@
   Metrics metrics_;  // This must be destroyed after service_ and wifi_service_
   MetricsLibraryMock library_;
   scoped_refptr<MockService> service_;
-  WiFiRefPtr wifi_;
   const std::vector<uint8_t> ssid_;
   scoped_refptr<MockWiFiService> wifi_service_;
 };
diff --git a/mock_manager.h b/mock_manager.h
index 1101e22..8c946d6 100644
--- a/mock_manager.h
+++ b/mock_manager.h
@@ -46,13 +46,15 @@
   MOCK_CONST_METHOD0(GetDefaultService, ServiceRefPtr());
   MOCK_CONST_METHOD0(IsOnline, bool());
   MOCK_METHOD0(UpdateEnabledTechnologies, void());
-  MOCK_METHOD1(LoadDeviceFromProfiles, void(const DeviceRefPtr &device));
   MOCK_METHOD1(IsPortalDetectionEnabled, bool(Technology::Identifier tech));
   MOCK_CONST_METHOD1(IsServiceEphemeral,
                      bool(const ServiceConstRefPtr &service));
+  MOCK_CONST_METHOD1(IsTechnologyConnected,
+                     bool(Technology::Identifier tech));
   MOCK_CONST_METHOD1(IsTechnologyLinkMonitorEnabled,
                      bool(Technology::Identifier tech));
   MOCK_CONST_METHOD1(IsDefaultProfile, bool(const StoreInterface *storage));
+  MOCK_METHOD2(RequestScan, void(const std::string &technology, Error *error));
   MOCK_CONST_METHOD0(GetPortalCheckURL, const std::string &());
   MOCK_CONST_METHOD0(GetPortalCheckInterval, int());
 
diff --git a/mock_wifi.h b/mock_wifi.h
index 292b6d3..bdcdd31 100644
--- a/mock_wifi.h
+++ b/mock_wifi.h
@@ -40,14 +40,13 @@
                           const EnabledStateChangedCallback &callback));
   MOCK_METHOD1(Scan, void(Error *error));
   MOCK_METHOD1(DisconnectFrom, void(WiFiService *service));
-  MOCK_METHOD2(GetService,
-               WiFiServiceRefPtr(const KeyValueStore &args, Error *error));
   MOCK_METHOD1(ClearCachedCredentials, void(const WiFiService *service));
   MOCK_METHOD2(ConnectTo,
                void(WiFiService *service,
                     std::map<std::string, ::DBus::Variant> service_params));
   MOCK_CONST_METHOD0(IsIdle, bool());
-  MOCK_METHOD1(NotifyEndpointChanged, void(const WiFiEndpoint &endpoint));
+  MOCK_METHOD1(NotifyEndpointChanged,
+               void(const WiFiEndpointConstRefPtr &endpoint));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockWiFi);
diff --git a/mock_wifi_provider.cc b/mock_wifi_provider.cc
new file mode 100644
index 0000000..b4cbac7
--- /dev/null
+++ b/mock_wifi_provider.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2013 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 "shill/mock_wifi_provider.h"
+
+#include "shill/wifi_service.h"  // Needed for mock method instantiation.
+
+using testing::Return;
+
+namespace shill {
+
+MockWiFiProvider::MockWiFiProvider() : WiFiProvider(NULL, NULL, NULL, NULL) {
+  ON_CALL(*this, GetHiddenSSIDList()).WillByDefault(Return(ByteArrays()));
+}
+
+MockWiFiProvider::~MockWiFiProvider() {}
+
+}  // namespace shill
diff --git a/mock_wifi_provider.h b/mock_wifi_provider.h
new file mode 100644
index 0000000..abab786
--- /dev/null
+++ b/mock_wifi_provider.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 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.
+
+#ifndef SHILL_MOCK_WIFI_PROVIDER_H_
+#define SHILL_MOCK_WIFI_PROVIDER_H_
+
+#include <gmock/gmock.h>
+
+#include "shill/wifi_endpoint.h"
+#include "shill/wifi_provider.h"
+
+namespace shill {
+
+class MockWiFiProvider : public WiFiProvider {
+ public:
+  MockWiFiProvider();
+  virtual ~MockWiFiProvider();
+
+  MOCK_METHOD0(Start, void());
+  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD1(CreateServicesFromProfile, void(const ProfileRefPtr &profile));
+  MOCK_METHOD2(GetService, WiFiServiceRefPtr(const KeyValueStore &args,
+                                             Error *error));
+  MOCK_METHOD1(FindServiceForEndpoint,
+               WiFiServiceRefPtr(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(OnEndpointAdded, void(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(OnEndpointRemoved,
+               WiFiServiceRefPtr(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(OnServiceUnloaded, bool(const WiFiServiceRefPtr &service));
+  MOCK_METHOD0(GetHiddenSSIDList, ByteArrays());
+  MOCK_METHOD2(FixupServiceEntries, void(StoreInterface *storage,
+                                         bool is_default_profile));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockWiFiProvider);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_WIFI_PROVIDER_H_
diff --git a/mock_wifi_service.cc b/mock_wifi_service.cc
index 0066d73..e5a7216 100644
--- a/mock_wifi_service.cc
+++ b/mock_wifi_service.cc
@@ -17,13 +17,13 @@
                                  EventDispatcher *dispatcher,
                                  Metrics *metrics,
                                  Manager *manager,
-                                 const WiFiRefPtr &device,
+                                 WiFiProvider *provider,
                                  const vector<uint8_t> &ssid,
                                  const string &mode,
                                  const string &security,
                                  bool hidden_ssid)
     : WiFiService(
-        control_interface, dispatcher, metrics, manager, device, ssid, mode,
+        control_interface, dispatcher, metrics, manager, provider, ssid, mode,
         security, hidden_ssid) {}
 
 MockWiFiService::~MockWiFiService() {}
diff --git a/mock_wifi_service.h b/mock_wifi_service.h
index c4168cb..0078f9d 100644
--- a/mock_wifi_service.h
+++ b/mock_wifi_service.h
@@ -20,7 +20,7 @@
                   EventDispatcher *dispatcher,
                   Metrics *metrics,
                   Manager *manager,
-                  const WiFiRefPtr &device,
+                  WiFiProvider *provider,
                   const std::vector<uint8_t> &ssid,
                   const std::string &mode,
                   const std::string &security,
@@ -33,6 +33,22 @@
   MOCK_METHOD2(AddEAPCertification, bool(const std::string &name,
                                          size_t depth));
   MOCK_METHOD0(HasRecentConnectionIssues, bool());
+  MOCK_METHOD1(AddEndpoint,
+               void(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(RemoveEndpoint,
+               void(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(NotifyCurrentEndpoint,
+               void(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD1(NotifyEndpointUpdated,
+               void(const WiFiEndpointConstRefPtr &endpoint));
+  MOCK_METHOD2(DisconnectWithFailure,
+               void(ConnectFailure failure, Error *error));
+  MOCK_CONST_METHOD0(IsConnected, bool());
+  MOCK_CONST_METHOD0(IsConnecting, bool());
+  MOCK_CONST_METHOD0(GetEndpointCount, int());
+  MOCK_CONST_METHOD0(HasEndpoints, bool());
+  MOCK_CONST_METHOD0(IsRemembered, bool());
+  MOCK_METHOD0(ResetWiFi, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockWiFiService);
diff --git a/profile.cc b/profile.cc
index e73f784..51668c0 100644
--- a/profile.cc
+++ b/profile.cc
@@ -104,6 +104,7 @@
                            name_.identifier.c_str()));
   }
   set_storage(storage.release());
+  manager_->OnProfileStorageInitialized(storage_.get());
   return true;
 }
 
diff --git a/service.h b/service.h
index e63a013..16342ad 100644
--- a/service.h
+++ b/service.h
@@ -566,7 +566,6 @@
   FRIEND_TEST(ServiceTest, SetProperty);
   FRIEND_TEST(ServiceTest, State);
   FRIEND_TEST(ServiceTest, Unload);
-  FRIEND_TEST(WiFiMainTest, NoScansWhileConnecting);
   FRIEND_TEST(WiFiMainTest, SuspectCredentialsEAPInProgress);
   FRIEND_TEST(WiFiMainTest, SuspectCredentialsWPAPreviouslyConnected);
   FRIEND_TEST(WiFiTimerTest, ReconnectTimer);
diff --git a/wifi.cc b/wifi.cc
index 6ab1a31..d6e6484 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -11,13 +11,11 @@
 
 #include <algorithm>
 #include <map>
-#include <set>
 #include <string>
 #include <vector>
 
 #include <base/bind.h>
 #include <base/stringprintf.h>
-#include <base/string_number_conversions.h>
 #include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
 #include <glib.h>
@@ -27,33 +25,29 @@
 #include "shill/dbus_adaptor.h"
 #include "shill/device.h"
 #include "shill/error.h"
-#include "shill/event_dispatcher.h"
 #include "shill/geolocation_info.h"
-#include "shill/key_value_store.h"
 #include "shill/ieee80211.h"
 #include "shill/link_monitor.h"
 #include "shill/logging.h"
 #include "shill/manager.h"
 #include "shill/metrics.h"
-#include "shill/profile.h"
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
 #include "shill/rtnl_handler.h"
 #include "shill/scope_logger.h"
 #include "shill/shill_time.h"
-#include "shill/store_interface.h"
 #include "shill/supplicant_interface_proxy_interface.h"
 #include "shill/supplicant_network_proxy_interface.h"
 #include "shill/supplicant_process_proxy_interface.h"
 #include "shill/technology.h"
 #include "shill/wifi_endpoint.h"
+#include "shill/wifi_provider.h"
 #include "shill/wifi_service.h"
 #include "shill/wpa_supplicant.h"
 
 using base::Bind;
 using base::StringPrintf;
 using std::map;
-using std::set;
 using std::string;
 using std::vector;
 
@@ -68,16 +62,6 @@
 const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
 // Scan interval while connected.
 const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
-// Note that WiFi generates some manager-level errors, because it implements
-// the Manager.GetWiFiService flimflam API. The API is implemented here,
-// rather than in manager, to keep WiFi-specific logic in the right place.
-const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
-const char WiFi::kManagerErrorSSIDTooLong[]  = "SSID is too long";
-const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
-const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
-    "security mode is unsupported";
-const char WiFi::kManagerErrorUnsupportedServiceMode[] =
-    "service mode is unsupported";
 // Age (in seconds) beyond which a BSS cache entry will not be preserved,
 // across a suspend/resume.
 const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
@@ -103,6 +87,7 @@
              address,
              interface_index,
              Technology::kWifi),
+      provider_(manager->wifi_provider()),
       weak_ptr_factory_(this),
       proxy_factory_(ProxyFactory::GetInstance()),
       time_(Time::GetInstance()),
@@ -185,27 +170,22 @@
   if (config80211) {
     config80211->SetWifiState(Config80211::kWifiUp);
   }
-
-  // Ensure that hidden services are loaded from profiles.  The may have been
-  // removed with a previous call to Stop().
-  manager()->LoadDeviceFromProfiles(this);
 }
 
 void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
   SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
   DropConnection();
   StopScanTimer();
+  for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
+       it != endpoint_by_rpcid_.end(); ++it) {
+    provider_->OnEndpointRemoved(it->second);
+  }
   endpoint_by_rpcid_.clear();
-
-  for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
-       it != services_.end();
-       ++it) {
-    SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
-                  << (*it)->unique_name();
-    manager()->DeregisterService(*it);
+  for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
+       it != rpcid_by_service_.end(); ++it) {
+    RemoveNetwork(it->second);
   }
   rpcid_by_service_.clear();
-  services_.clear();                  // breaks reference cycles
   supplicant_interface_proxy_.reset();  // breaks a reference cycle
   // TODO(quiche): Remove interface from supplicant.
   supplicant_process_proxy_.reset();
@@ -231,13 +211,6 @@
                 << (pending_service_.get() ? "is set." : "is not set.");
   SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
                 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
-  SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
-                << " Services.";
-}
-
-bool WiFi::Load(StoreInterface *storage) {
-  LoadHiddenServices(storage);
-  return Device::Load(storage);
 }
 
 void WiFi::Scan(Error */*error*/) {
@@ -461,8 +434,8 @@
   RemoveNetworkForService(service, &unused_error);
 }
 
-void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
-  WiFiService *service = FindServiceForEndpoint(endpoint);
+void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
+  WiFiService *service = provider_->FindServiceForEndpoint(endpoint);
   DCHECK(service);
   if (service) {
     service->NotifyEndpointUpdated(endpoint);
@@ -553,24 +526,6 @@
   bgscan_method_.clear();
 }
 
-// To avoid creating duplicate services, call FindServiceForEndpoint
-// before calling this method.
-WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
-                                                 bool hidden_ssid) {
-  WiFiServiceRefPtr service =
-      new WiFiService(control_interface(),
-                      dispatcher(),
-                      metrics(),
-                      manager(),
-                      this,
-                      endpoint.ssid(),
-                      endpoint.network_mode(),
-                      endpoint.security_mode(),
-                      hidden_ssid);
-  services_.push_back(service);
-  return service;
-}
-
 void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
   SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
                 << supplicant_bss_ << " -> " << new_bss;
@@ -585,7 +540,7 @@
 
   if (new_bss == wpa_supplicant::kCurrentBSSNull) {
     HandleDisconnect();
-    if (!GetHiddenSSIDList().empty()) {
+    if (!provider_->GetHiddenSSIDList().empty()) {
       // Before disconnecting, wpa_supplicant probably scanned for
       // APs. So, in the normal case, we defer to the timer for the next scan.
       //
@@ -715,21 +670,21 @@
     return;
   }
 
-  const WiFiEndpoint &endpoint(*endpoint_it->second);
-  WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
+  const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
+  WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
   if (!service.get()) {
       LOG(WARNING) << "WiFi " << link_name()
                    << " could not find Service for Endpoint "
-                   << endpoint.bssid_string()
+                   << endpoint->bssid_string()
                    << " (service will be unchanged)";
       return;
   }
 
   SLOG(WiFi, 2) << "WiFi " << link_name()
-                << " roamed to Endpoint " << endpoint.bssid_string()
-                << " " << LogSSID(endpoint.ssid_string());
+                << " roamed to Endpoint " << endpoint->bssid_string()
+                << " " << LogSSID(endpoint->ssid_string());
 
-  service->NotifyCurrentEndpoint(&endpoint);
+  service->NotifyCurrentEndpoint(endpoint);
 
   if (pending_service_.get() &&
       service.get() != pending_service_.get()) {
@@ -745,7 +700,7 @@
     // So we leave |pending_service_| untouched.
     SLOG(WiFi, 2) << "WiFi " << link_name()
                   << " new current Endpoint "
-                  << endpoint.bssid_string()
+                  << endpoint->bssid_string()
                   << " is not part of pending service "
                   << pending_service_->unique_name();
 
@@ -754,7 +709,7 @@
     if (service.get() != current_service_.get()) {
       LOG(WARNING) << "WiFi " << link_name()
                    << " new current Endpoint "
-                   << endpoint.bssid_string()
+                   << endpoint->bssid_string()
                    << " is neither part of pending service "
                    << pending_service_->unique_name()
                    << " nor part of current service "
@@ -786,7 +741,7 @@
     LOG(WARNING)
         << "WiFi " << link_name()
         << " new current Endpoint "
-        << endpoint.bssid_string()
+        << endpoint->bssid_string()
         << (current_service_.get() ?
             StringPrintf(" is not part of current service %s",
                          current_service_->unique_name().c_str()) :
@@ -812,26 +767,6 @@
   return;
 }
 
-WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
-                                    const string &mode,
-                                    const string &security) const {
-  for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
-       it != services_.end();
-       ++it) {
-    if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
-        (*it)->IsSecurityMatch(security)) {
-      return *it;
-    }
-  }
-  return NULL;
-}
-
-WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
-  return FindService(endpoint.ssid(),
-                     endpoint.network_mode(),
-                     endpoint.security_mode());
-}
-
 string WiFi::FindNetworkRpcidForService(
     const WiFiService *service, Error *error) {
   ReverseServiceMap::const_iterator rpcid_it =
@@ -905,135 +840,6 @@
   return true;
 }
 
-ByteArrays WiFi::GetHiddenSSIDList() {
-  // Create a unique set of hidden SSIDs.
-  set<ByteArray> hidden_ssids_set;
-  for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
-       it != services_.end();
-       ++it) {
-    if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
-      hidden_ssids_set.insert((*it)->ssid());
-    }
-  }
-  SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
-  ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
-  if (!hidden_ssids.empty()) {
-    // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
-    // not fit in.
-    if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
-      hidden_ssids.erase(
-          hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
-          hidden_ssids.end());
-    }
-    // Add Broadcast SSID, signified by an empty ByteArray.  If we specify
-    // SSIDs to wpa_supplicant, we need to explicitly specify the default
-    // behavior of doing a broadcast probe.
-    hidden_ssids.push_back(ByteArray());
-  }
-  return hidden_ssids;
-}
-
-bool WiFi::LoadHiddenServices(StoreInterface *storage) {
-  // TODO(pstew): This retrofits old (flimflam-based) profile entries for
-  // WiFi Services so they contain the properties that are required to
-  // be searched for by property instead of by group name.  I can imagine
-  // a day where we believe that enough users have upgraded so that this
-  // code is no longer necessary.  The UMA metric below should help us
-  // understand when this point might be.
-  if (WiFiService::FixupServiceEntries(storage)) {
-    storage->Flush();
-    Metrics::ServiceFixupProfileType profile_type =
-        manager()->IsDefaultProfile(storage) ?
-            Metrics::kMetricServiceFixupDefaultProfile :
-            Metrics::kMetricServiceFixupUserProfile;
-    metrics()->SendEnumToUMA(
-        metrics()->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
-                                     technology()),
-        profile_type,
-        Metrics::kMetricServiceFixupMax);
-  }
-  bool created_hidden_service = false;
-  set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
-  for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
-    bool is_hidden = false;
-    if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
-      SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
-                    << "GetGroupsWithKey failed for GetBool("
-                    << flimflam::kWifiHiddenSsid
-                    << ") -- possible non-bool key";
-      continue;
-    }
-    if (!is_hidden) {
-      continue;
-    }
-    string ssid_hex;
-    vector<uint8_t> ssid_bytes;
-    if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
-        !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
-      SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
-                    << flimflam::kSSIDProperty << "\" property";
-      continue;
-    }
-    string device_address;
-    string network_mode;
-    string security;
-    // It is gross that we have to do this, but the only place we can
-    // get information about the service is from its storage name.
-    if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
-                                             &network_mode, &security) ||
-        device_address != address()) {
-      SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
-                    << *it << "\"";
-      continue;
-    }
-    if (FindService(ssid_bytes, network_mode, security).get()) {
-      // If service already exists, we have nothing to do, since the
-      // service has already loaded its configuration from storage.
-      // This is guaranteed to happen in both cases where Load() is
-      // called on a device (via a ConfigureDevice() call on the
-      // profile):
-      //  - In RegisterDevice() the Device hasn't been started yet,
-      //    so it has no services registered, except for those
-      //    created by previous iterations of LoadHiddenService().
-      //    The latter can happen if another profile in the Manager's
-      //    stack defines the same ssid/mode/security.  Even this
-      //    case is okay, since even if the profiles differ
-      //    materially on configuration and credentials, the "right"
-      //    one will be configured in the course of the
-      //    RegisterService() call below.
-      // - In PushProfile(), all registered services have been
-      //   introduced to the profile via ConfigureService() prior
-      //   to calling ConfigureDevice() on the profile.
-      continue;
-    }
-    WiFiServiceRefPtr service(new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              this,
-                                              ssid_bytes,
-                                              network_mode,
-                                              security,
-                                              true));
-    services_.push_back(service);
-
-    // By registering the service, the rest of the configuration
-    // will be loaded from the profile into the service via ConfigureService().
-    manager()->RegisterService(service);
-
-    created_hidden_service = true;
-  }
-
-  // If we are idle and we created a service as a result of opening the
-  // profile, we should initiate a scan for our new hidden SSID.
-  if (running() && created_hidden_service &&
-      supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
-    Scan(NULL);
-  }
-
-  return created_hidden_service;
-}
-
 void WiFi::BSSAddedTask(
     const ::DBus::Path &path,
     const map<string, ::DBus::Variant> &properties) {
@@ -1062,25 +868,7 @@
     return;
   }
 
-  WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
-  if (service) {
-    SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
-                  << " to service " << service->unique_name() << ".";
-    service->AddEndpoint(endpoint);
-
-    if (manager()->HasService(service)) {
-      manager()->UpdateService(service);
-    } else {
-      // Expect registered by now if >1.
-      DCHECK_EQ(1, service->GetEndpointCount());
-      manager()->RegisterService(service);
-    }
-  } else {
-    const bool hidden_ssid = false;
-    service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
-    service->AddEndpoint(endpoint);
-    manager()->RegisterService(service);
-  }
+  provider_->OnEndpointAdded(endpoint);
 
   // Do this last, to maintain the invariant that any Endpoint we
   // know about has a corresponding Service.
@@ -1106,46 +894,19 @@
   CHECK(endpoint);
   endpoint_by_rpcid_.erase(i);
 
-  WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
-  CHECK(service) << "Can't find Service for Endpoint "
-                 << path << " "
-                 << "(with BSSID " << endpoint->bssid_string() << ").";
-  SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
-                << " from Service " << service->unique_name();
-  service->RemoveEndpoint(endpoint);
+  WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
+  if (!service) {
+    return;
+  }
+  Error unused_error;
+  RemoveNetworkForService(service, &unused_error);
 
   bool disconnect_service = !service->HasEndpoints() &&
       (service->IsConnecting() || service->IsConnected());
-  bool forget_service =
-      // Forget Services without Endpoints, except that we always keep
-      // hidden services around. (We need them around to populate the
-      // hidden SSIDs list.)
-      !service->HasEndpoints() && !service->hidden_ssid();
-  bool deregister_service =
-      // Only deregister a Service if we're forgetting it. Otherwise,
-      // Manager can't keep our configuration up-to-date (as Profiles
-      // change).
-      forget_service;
 
   if (disconnect_service) {
     DisconnectFrom(service);
   }
-
-  if (deregister_service) {
-    manager()->DeregisterService(service);
-  } else {
-    manager()->UpdateService(service);
-  }
-
-  if (forget_service) {
-    Error unused_error;
-    RemoveNetworkForService(service, &unused_error);
-    vector<WiFiServiceRefPtr>::iterator it;
-    it = std::find(services_.begin(), services_.end(), service);
-    if (it != services_.end()) {
-      services_.erase(it);
-    }
-  }
 }
 
 void WiFi::CertificationTask(
@@ -1290,13 +1051,24 @@
   scan_args[wpa_supplicant::kPropertyScanType].writer().
       append_string(wpa_supplicant::kScanTypeActive);
 
-  ByteArrays hidden_ssids = GetHiddenSSIDList();
+  ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
   if (!hidden_ssids.empty()) {
+    // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
+    // not fit in.
+    if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
+      hidden_ssids.erase(
+          hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
+          hidden_ssids.end());
+    }
+    // Add Broadcast SSID, signified by an empty ByteArray.  If we specify
+    // SSIDs to wpa_supplicant, we need to explicitly specify the default
+    // behavior of doing a broadcast probe.
+    hidden_ssids.push_back(ByteArray());
+
     scan_args[wpa_supplicant::kPropertyScanSSIDs] =
         DBusAdaptor::ByteArraysToVariant(hidden_ssids);
   }
 
-  // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
   try {
     supplicant_interface_proxy_->Scan(scan_args);
     SetScanPending(true);
@@ -1406,88 +1178,6 @@
   return false;
 }
 
-// Used by Manager.
-WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
-  CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
-
-  if (args.ContainsString(flimflam::kModeProperty) &&
-      args.GetString(flimflam::kModeProperty) !=
-      flimflam::kModeManaged) {
-    Error::PopulateAndLog(error, Error::kNotSupported,
-                          kManagerErrorUnsupportedServiceMode);
-    return NULL;
-  }
-
-  if (!args.ContainsString(flimflam::kSSIDProperty)) {
-    Error::PopulateAndLog(error, Error::kInvalidArguments,
-                          kManagerErrorSSIDRequired);
-    return NULL;
-  }
-
-  string ssid = args.GetString(flimflam::kSSIDProperty);
-  if (ssid.length() < 1) {
-    Error::PopulateAndLog(error, Error::kInvalidNetworkName,
-                          kManagerErrorSSIDTooShort);
-    return NULL;
-  }
-
-  if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
-    Error::PopulateAndLog(error, Error::kInvalidNetworkName,
-                          kManagerErrorSSIDTooLong);
-    return NULL;
-  }
-
-  string security_method;
-  if (args.ContainsString(flimflam::kSecurityProperty)) {
-    security_method = args.GetString(flimflam::kSecurityProperty);
-  } else {
-    security_method = flimflam::kSecurityNone;
-  }
-
-  if (security_method != flimflam::kSecurityNone &&
-      security_method != flimflam::kSecurityWep &&
-      security_method != flimflam::kSecurityPsk &&
-      security_method != flimflam::kSecurityWpa &&
-      security_method != flimflam::kSecurityRsn &&
-      security_method != flimflam::kSecurity8021x) {
-    Error::PopulateAndLog(error, Error::kNotSupported,
-                          kManagerErrorUnsupportedSecurityMode);
-    return NULL;
-  }
-
-  bool hidden_ssid;
-  if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
-    hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
-  } else {
-    // If the service is not found, and the caller hasn't specified otherwise,
-    // we assume this is is a hidden network.
-    hidden_ssid = true;
-  }
-
-  vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
-  WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
-                                        security_method));
-  if (!service.get()) {
-    service = new WiFiService(control_interface(),
-                              dispatcher(),
-                              metrics(),
-                              manager(),
-                              this,
-                              ssid_bytes,
-                              flimflam::kModeManaged,
-                              security_method,
-                              hidden_ssid);
-    services_.push_back(service);
-    // NB: We do not register the newly created Service with the Manager.
-    // The Service will be registered if/when we find Endpoints for it.
-  }
-
-  // TODO(pstew): Schedule a task to forget up all non-hidden services that
-  // have no endpoints like the one we may have just created.  crosbug.com/28224
-
-  return service;
-}
-
 // static
 bool WiFi::SanitizeSSID(string *ssid) {
   CHECK(ssid);
@@ -1554,6 +1244,15 @@
   return true;
 }
 
+void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
+  DisconnectFrom(service);
+  if (service == selected_service()) {
+    DropConnection();
+  }
+  Error unused_error;
+  RemoveNetworkForService(service, &unused_error);
+}
+
 vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
   vector<GeolocationInfo> objects;
   for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
diff --git a/wifi.h b/wifi.h
index c718b9a..65e00c3 100644
--- a/wifi.h
+++ b/wifi.h
@@ -22,13 +22,6 @@
 // the value is a pointer to a WiFiEndpoint object.  When a WiFiEndpoint is
 // added, it is associated with a WiFiService.
 //
-// A WiFi device becomes aware of a WiFiService in three different ways.  1)
-// When a WiFiEndpoint is added through the BSSAdded signal, the WiFiEndpoint is
-// providing a service, and if that service is unknown to the WiFi device, it is
-// added at that point.  2) The Manager can add a WiFiService by calling
-// WiFi::GetService().  3) Services are loaded from the profile through a call
-// to WiFi::Load().
-//
 // The WiFi device connects to a WiFiService, not a WiFiEndpoint, through WPA
 // Supplicant. It is the job of WPA Supplicant to select a BSS (aka
 // WiFiEndpoint) to connect to.  The protocol for establishing a connection is
@@ -107,6 +100,7 @@
 class ProxyFactory;
 class SupplicantInterfaceProxyInterface;
 class SupplicantProcessProxyInterface;
+class WiFiProvider;
 class WiFiService;
 
 // WiFi class. Specialization of Device for WiFi.
@@ -123,7 +117,6 @@
 
   virtual void Start(Error *error, const EnabledStateChangedCallback &callback);
   virtual void Stop(Error *error, const EnabledStateChangedCallback &callback);
-  virtual bool Load(StoreInterface *storage);
   virtual void Scan(Error *error);
   // Callback for system resume. If this WiFi device is idle, a scan
   // is initiated. Additionally, the base class implementation is
@@ -158,10 +151,7 @@
   virtual void ClearCachedCredentials(const WiFiService *service);
 
   // Called by WiFiEndpoint.
-  virtual void NotifyEndpointChanged(const WiFiEndpoint &endpoint);
-
-  // Called by Manager.
-  virtual WiFiServiceRefPtr GetService(const KeyValueStore &args, Error *error);
+  virtual void NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint);
 
   // Utility, used by WiFiService and WiFiEndpoint.
   // Replace non-ASCII characters with '?'. Return true if one or more
@@ -184,6 +174,9 @@
   // Overridden from Device superclass
   virtual bool ShouldUseArpGateway() const;
 
+  // Called by a WiFiService when it disassociates itself from this Device.
+  virtual void DisassociateFromService(const WiFiServiceRefPtr &service);
+
  private:
   friend class WiFiObjectTest;  // access to supplicant_*_proxy_, link_up_
   friend class WiFiTimerTest;  // kNumFastScanAttempts, kFastScanIntervalSeconds
@@ -208,11 +201,6 @@
   static const int32 kDefaultBgscanSignalThresholdDbm;
   static const uint16 kDefaultScanIntervalSeconds;
   static const uint16 kBackgroundScanIntervalSeconds;
-  static const char kManagerErrorSSIDTooLong[];
-  static const char kManagerErrorSSIDTooShort[];
-  static const char kManagerErrorSSIDRequired[];
-  static const char kManagerErrorUnsupportedSecurityMode[];
-  static const char kManagerErrorUnsupportedServiceMode[];
   static const time_t kMaxBSSResumeAgeSeconds;
   static const char kInterfaceStateUnknown[];
   // Delay between scans when supplicant finds "No suitable network".
@@ -240,24 +228,14 @@
   void SetScanInterval(const uint16 &seconds, Error *error);
   void ClearBgscanMethod(const int &argument, Error *error);
 
-  WiFiServiceRefPtr CreateServiceForEndpoint(
-      const WiFiEndpoint &endpoint, bool hidden_ssid);
   void CurrentBSSChanged(const ::DBus::Path &new_bss);
-  WiFiServiceRefPtr FindService(const std::vector<uint8_t> &ssid,
-                                const std::string &mode,
-                                const std::string &security) const;
-  WiFiServiceRefPtr FindServiceForEndpoint(const WiFiEndpoint &endpoint);
   // Return the RPC identifier associated with the wpa_supplicant network
   // entry created for |service|.  If one does not exist, an empty string
   // is returned, and |error| is populated.
   std::string FindNetworkRpcidForService(const WiFiService *service,
                                          Error *error);
-  ByteArrays GetHiddenSSIDList();
   void HandleDisconnect();
   void HandleRoam(const ::DBus::Path &new_bssid);
-  // Create services for hidden networks stored in |storage|.  Returns true
-  // if any were found, otherwise returns false.
-  bool LoadHiddenServices(StoreInterface *storage);
   void BSSAddedTask(const ::DBus::Path &BSS,
                     const std::map<std::string, ::DBus::Variant> &properties);
   void BSSRemovedTask(const ::DBus::Path &BSS);
@@ -351,6 +329,9 @@
 
   void Restart();
 
+  // Pointer to the provider object that maintains WiFiService objects.
+  WiFiProvider *provider_;
+
   base::WeakPtrFactory<WiFi> weak_ptr_factory_;
 
   // Store cached copies of singletons for speed/ease of testing.
@@ -369,7 +350,6 @@
   // Map from Services to the D-Bus path for the corresponding wpa_supplicant
   // Network.
   ReverseServiceMap rpcid_by_service_;
-  std::vector<WiFiServiceRefPtr> services_;
   // The Service we are presently connected to. May be NULL is we're not
   // not connected to any Service.
   WiFiServiceRefPtr current_service_;
diff --git a/wifi_endpoint.cc b/wifi_endpoint.cc
index 27db09e..d5b54cd 100644
--- a/wifi_endpoint.cc
+++ b/wifi_endpoint.cc
@@ -92,7 +92,7 @@
     signal_strength_ = properties_it->second.reader().get_int16();
     SLOG(WiFi, 2) << "WiFiEndpoint " << bssid_string_ << " signal is now "
                   << signal_strength_;
-    device_->NotifyEndpointChanged(*this);
+    device_->NotifyEndpointChanged(this);
   }
 }
 
@@ -163,6 +163,10 @@
   return bssid_hex_;
 }
 
+const WiFiRefPtr &WiFiEndpoint::device() const {
+  return device_;
+}
+
 int16_t WiFiEndpoint::signal_strength() const {
   return signal_strength_;
 }
@@ -192,6 +196,7 @@
                                              const WiFiRefPtr &wifi,
                                              const string &ssid,
                                              const string &bssid,
+                                             const string &network_mode,
                                              uint16 frequency,
                                              int16 signal_dbm) {
   map <string, ::DBus::Variant> args;
@@ -210,7 +215,7 @@
   args[wpa_supplicant::kBSSPropertySignal].writer().append_int16(signal_dbm);
   args[wpa_supplicant::kBSSPropertyFrequency].writer().append_uint16(frequency);
   args[wpa_supplicant::kBSSPropertyMode].writer().append_string(
-      wpa_supplicant::kNetworkModeInfrastructure);
+      network_mode.c_str());
   // We indicate this is an open BSS by leaving out all security properties.
 
   return new WiFiEndpoint(
diff --git a/wifi_endpoint.h b/wifi_endpoint.h
index b6cdcbe..ae32ac5 100644
--- a/wifi_endpoint.h
+++ b/wifi_endpoint.h
@@ -62,6 +62,7 @@
   const std::string &ssid_hex() const;
   const std::string &bssid_string() const;
   const std::string &bssid_hex() const;
+  const WiFiRefPtr &device() const;
   int16_t signal_strength() const;
   uint16 frequency() const;
   uint16 physical_mode() const;
@@ -72,6 +73,7 @@
  private:
   friend class WiFiEndpointTest;
   friend class WiFiObjectTest;  // for MakeOpenEndpoint
+  friend class WiFiProviderTest;  // for MakeOpenEndpoint
   friend class WiFiServiceTest;  // for MakeOpenEndpoint
   // these test cases need access to the KeyManagement enum
   FRIEND_TEST(WiFiEndpointTest, DeterminePhyModeFromFrequency);
@@ -95,6 +97,7 @@
                                         const WiFiRefPtr &wifi,
                                         const std::string &ssid,
                                         const std::string &bssid,
+                                        const std::string &network_mode,
                                         uint16 frequency,
                                         int16 signal_dbm);
   // Maps mode strings from supplicant into flimflam's nomenclature, as defined
diff --git a/wifi_endpoint_unittest.cc b/wifi_endpoint_unittest.cc
index d1b6d7b..20e6e3b 100644
--- a/wifi_endpoint_unittest.cc
+++ b/wifi_endpoint_unittest.cc
@@ -158,7 +158,8 @@
                                  const std::string &ssid,
                                  const std::string &bssid) {
     return WiFiEndpoint::MakeOpenEndpoint(
-        proxy_factory, wifi, ssid, bssid, 0, 0);
+        proxy_factory, wifi, ssid, bssid,
+        wpa_supplicant::kNetworkModeInfrastructure, 0, 0);
   }
 
   scoped_refptr<MockWiFi> wifi() { return wifi_; }
diff --git a/wifi_provider.cc b/wifi_provider.cc
index 8f0a0cd..6e7602b 100644
--- a/wifi_provider.cc
+++ b/wifi_provider.cc
@@ -4,16 +4,45 @@
 
 #include "shill/wifi_provider.h"
 
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/string_number_conversions.h>
+
 #include "shill/error.h"
 #include "shill/event_dispatcher.h"
+#include "shill/ieee80211.h"
 #include "shill/key_value_store.h"
+#include "shill/logging.h"
 #include "shill/manager.h"
 #include "shill/metrics.h"
+#include "shill/profile.h"
+#include "shill/store_interface.h"
+#include "shill/technology.h"
 #include "shill/wifi_endpoint.h"
 #include "shill/wifi_service.h"
 
+using base::Bind;
+using std::set;
+using std::string;
+using std::vector;
+
 namespace shill {
 
+// Note that WiFiProvider generates some manager-level errors, because it
+// implements the WiFi portion of the Manager.GetService flimflam API. The
+// API is implemented here, rather than in manager, to keep WiFi-specific
+// logic in the right place.
+const char WiFiProvider::kManagerErrorSSIDRequired[] = "must specify SSID";
+const char WiFiProvider::kManagerErrorSSIDTooLong[]  = "SSID is too long";
+const char WiFiProvider::kManagerErrorSSIDTooShort[] = "SSID is too short";
+const char WiFiProvider::kManagerErrorUnsupportedSecurityMode[] =
+    "security mode is unsupported";
+const char WiFiProvider::kManagerErrorUnsupportedServiceMode[] =
+    "service mode is unsupported";
+
 WiFiProvider::WiFiProvider(ControlInterface *control_interface,
                            EventDispatcher *dispatcher,
                            Metrics *metrics,
@@ -25,28 +54,273 @@
 
 WiFiProvider::~WiFiProvider() {}
 
-void WiFiProvider::Start() {
-  NOTIMPLEMENTED();
-}
+void WiFiProvider::Start() {}
 
 void WiFiProvider::Stop() {
-  NOTIMPLEMENTED();
+  SLOG(WiFi, 2) << __func__;
+  while (!services_.empty()) {
+    WiFiServiceRefPtr service = services_.back();
+    ForgetService(service);
+    SLOG(WiFi, 3) << "WiFiProvider deregistering service "
+                  << service->unique_name();
+    manager_->DeregisterService(service);
+  }
 }
 
-void WiFiProvider::CreateServicesFromProfile(ProfileRefPtr profile) {
-  NOTIMPLEMENTED();
+void WiFiProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
+  const StoreInterface *storage = profile->GetConstStorage();
+  KeyValueStore args;
+  args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
+  set<string> groups = storage->GetGroupsWithProperties(args);
+  bool created_hidden_service = false;
+  for (set<string>::const_iterator it = groups.begin(); it != groups.end();
+       ++it) {
+    string ssid_hex;
+    vector<uint8_t> ssid_bytes;
+    if (!storage->GetString(*it, WiFiService::kStorageSSID, &ssid_hex) ||
+        !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
+      SLOG(WiFi, 2) << "Storage group " << *it << " is missing valid \""
+                    << WiFiService::kStorageSSID << "\" property";
+      continue;
+    }
+    string network_mode;
+    if (!storage->GetString(*it, WiFiService::kStorageMode, &network_mode) ||
+        network_mode.empty()) {
+      SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
+                    <<  WiFiService::kStorageMode << "\" property";
+      continue;
+    }
+    string security;
+    if (!storage->GetString(*it, WiFiService::kStorageSecurity, &security) ||
+        !WiFiService::IsValidSecurityMethod(security)) {
+      SLOG(WiFi, 2) << "Storage group " << *it << " has missing or invalid \""
+                    <<  WiFiService::kStorageSecurity << "\" property";
+      continue;
+    }
+    bool is_hidden = false;
+    if (!storage->GetBool(*it, WiFiService::kStorageHiddenSSID, &is_hidden)) {
+      SLOG(WiFi, 2) << "Storage group " << *it << " is missing \""
+                    << WiFiService::kStorageHiddenSSID << "\" property";
+      continue;
+    }
+
+    if (FindService(ssid_bytes, network_mode, security)) {
+      // If service already exists, we have nothing to do, since the
+      // service has already loaded its configuration from storage.
+      // This is guaranteed to happen in the single case where
+      // CreateServicesFromProfile() is called on a WiFiProvider from
+      // Manager::PushProfile():
+      continue;
+    }
+
+    AddService(ssid_bytes, network_mode, security, is_hidden);
+
+    // By registering the service in AddService, the rest of the configuration
+    // will be loaded from the profile into the service via ConfigureService().
+
+    if (is_hidden) {
+      created_hidden_service = true;
+    }
+  }
+
+  // If WiFi is unconnected and we created a hidden service as a result
+  // of opening the profile, we should initiate a WiFi scan, which will
+  // allow us to find any hidden services that we may have created.
+  if (created_hidden_service &&
+      !manager_->IsTechnologyConnected(Technology::kWifi)) {
+    Error unused_error;
+    manager_->RequestScan(flimflam::kTypeWifi, &unused_error);
+  }
 }
 
 WiFiServiceRefPtr WiFiProvider::GetService(
     const KeyValueStore &args, Error *error) {
-  NOTIMPLEMENTED();
-  return NULL;
+  CHECK_EQ(args.LookupString(flimflam::kTypeProperty, ""), flimflam::kTypeWifi);
+
+  if (args.LookupString(flimflam::kModeProperty, "") !=
+      flimflam::kModeManaged) {
+    Error::PopulateAndLog(error, Error::kNotSupported,
+                          kManagerErrorUnsupportedServiceMode);
+    return NULL;
+  }
+
+  if (!args.ContainsString(flimflam::kSSIDProperty)) {
+    Error::PopulateAndLog(error, Error::kInvalidArguments,
+                          kManagerErrorSSIDRequired);
+    return NULL;
+  }
+
+  string ssid = args.GetString(flimflam::kSSIDProperty);
+  if (ssid.length() < 1) {
+    Error::PopulateAndLog(error, Error::kInvalidNetworkName,
+                          kManagerErrorSSIDTooShort);
+    return NULL;
+  }
+
+  if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
+    Error::PopulateAndLog(error, Error::kInvalidNetworkName,
+                          kManagerErrorSSIDTooLong);
+    return NULL;
+  }
+
+  string security_method = args.LookupString(flimflam::kSecurityProperty,
+                                             flimflam::kSecurityNone);
+
+  if (!WiFiService::IsValidSecurityMethod(security_method)) {
+    Error::PopulateAndLog(error, Error::kNotSupported,
+                          kManagerErrorUnsupportedSecurityMode);
+    return NULL;
+  }
+
+  // If the service is not found, and the caller hasn't specified otherwise,
+  // we assume it is a hidden service.
+  bool hidden_ssid = args.LookupBool(flimflam::kWifiHiddenSsid, true);
+
+  vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
+  WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
+                                        security_method));
+  if (!service) {
+    service = AddService(ssid_bytes,
+                         flimflam::kModeManaged,
+                         security_method,
+                         hidden_ssid);
+  }
+
+  return service;
 }
 
 WiFiServiceRefPtr WiFiProvider::FindServiceForEndpoint(
-    const WiFiEndpoint &endpoint) {
-  NOTIMPLEMENTED();
+    const WiFiEndpointConstRefPtr &endpoint) {
+  return FindService(endpoint->ssid(),
+                     endpoint->network_mode(),
+                     endpoint->security_mode());
+}
+
+void WiFiProvider::OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint) {
+  WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
+  if (!service) {
+    const bool hidden_ssid = false;
+    service = AddService(endpoint->ssid(),
+                         endpoint->network_mode(),
+                         endpoint->security_mode(),
+                         hidden_ssid);
+  }
+
+  service->AddEndpoint(endpoint);
+
+  SLOG(WiFi, 1) << "Assigned endpoint " << endpoint->bssid_string()
+                << " to service " << service->unique_name() << ".";
+
+  manager_->UpdateService(service);
+}
+
+WiFiServiceRefPtr WiFiProvider::OnEndpointRemoved(
+    const WiFiEndpointConstRefPtr &endpoint) {
+  WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
+
+  CHECK(service) << "Can't find Service for Endpoint "
+                 << "(with BSSID " << endpoint->bssid_string() << ").";
+  SLOG(WiFi, 1) << "Removing endpoint " << endpoint->bssid_string()
+                << " from Service " << service->unique_name();
+  service->RemoveEndpoint(endpoint);
+
+  if (service->HasEndpoints() || service->IsRemembered()) {
+    // Keep services around if they are in a profile or have remaining
+    // endpoints.
+    manager_->UpdateService(service);
+    return NULL;
+  }
+
+  ForgetService(service);
+  manager_->DeregisterService(service);
+
+  return service;
+}
+
+bool WiFiProvider::OnServiceUnloaded(const WiFiServiceRefPtr &service) {
+  // If the service still has endpoints, it should remain in the service list.
+  if (service->HasEndpoints()) {
+    return false;
+  }
+
+  // This is the one place where we forget the service but do not also
+  // deregister the service with the manager.  However, by returning
+  // true below, the manager will do so itself.
+  ForgetService(service);
+  return true;
+}
+
+void WiFiProvider::FixupServiceEntries(
+    StoreInterface *storage, bool is_default_profile) {
+  if (WiFiService::FixupServiceEntries(storage)) {
+    storage->Flush();
+    Metrics::ServiceFixupProfileType profile_type =
+        is_default_profile ?
+            Metrics::kMetricServiceFixupDefaultProfile :
+            Metrics::kMetricServiceFixupUserProfile;
+    metrics_->SendEnumToUMA(
+        metrics_->GetFullMetricName(Metrics::kMetricServiceFixupEntries,
+                                    Technology::kWifi),
+        profile_type,
+        Metrics::kMetricServiceFixupMax);
+  }
+}
+
+WiFiServiceRefPtr WiFiProvider::AddService(const vector<uint8_t> &ssid,
+                                           const string &mode,
+                                           const string &security,
+                                           bool is_hidden) {
+  WiFiServiceRefPtr service = new WiFiService(control_interface_,
+                                              dispatcher_,
+                                              metrics_,
+                                              manager_,
+                                              this,
+                                              ssid,
+                                              mode,
+                                              security,
+                                              is_hidden);
+
+  services_.push_back(service);
+  manager_->RegisterService(service);
+  return service;
+}
+
+WiFiServiceRefPtr WiFiProvider::FindService(const vector<uint8_t> &ssid,
+                                            const string &mode,
+                                            const string &security) const {
+  for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
+       it != services_.end();
+       ++it) {
+    if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
+        (*it)->IsSecurityMatch(security)) {
+      return *it;
+    }
+  }
   return NULL;
 }
 
+ByteArrays WiFiProvider::GetHiddenSSIDList() {
+  // Create a unique set of hidden SSIDs.
+  set<ByteArray> hidden_ssids_set;
+  for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
+       it != services_.end();
+       ++it) {
+    if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
+      hidden_ssids_set.insert((*it)->ssid());
+    }
+  }
+  SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
+  return ByteArrays(hidden_ssids_set.begin(), hidden_ssids_set.end());
+}
+
+void WiFiProvider::ForgetService(const WiFiServiceRefPtr &service) {
+  vector<WiFiServiceRefPtr>::iterator it;
+  it = std::find(services_.begin(), services_.end(), service);
+  if (it == services_.end()) {
+    return;
+  }
+  (*it)->ResetWiFi();
+  services_.erase(it);
+}
+
 }  // namespace shill
diff --git a/wifi_provider.h b/wifi_provider.h
index 2d5aeea..4c4fab1 100644
--- a/wifi_provider.h
+++ b/wifi_provider.h
@@ -5,6 +5,7 @@
 #ifndef SHILL_WIFI_PROVIDER_
 #define SHILL_WIFI_PROVIDER_
 
+#include "shill/accessor_interface.h"  // for ByteArrays
 #include "shill/refptr_types.h"
 
 namespace shill {
@@ -15,6 +16,7 @@
 class KeyValueStore;
 class Manager;
 class Metrics;
+class StoreInterface;
 class WiFiEndpoint;
 class WiFiService;
 
@@ -33,20 +35,66 @@
   virtual void Stop();
 
   // Called by Manager.
-  void CreateServicesFromProfile(ProfileRefPtr profile);
+  virtual void CreateServicesFromProfile(const ProfileRefPtr &profile);
   virtual WiFiServiceRefPtr GetService(const KeyValueStore &args, Error *error);
 
-  // Called by WiFi device.
-  WiFiServiceRefPtr FindServiceForEndpoint(const WiFiEndpoint &endpoint);
+  // Find a Service this Endpoint should be associated with.
+  virtual WiFiServiceRefPtr FindServiceForEndpoint(
+      const WiFiEndpointConstRefPtr &endpoint);
+
+  // Find or create a Service for |endpoint| to be associated with.  This
+  // method first calls FindServiceForEndpoint, and failing this, creates
+  // a new Service.  It then associates |endpoint| with this service.
+  virtual void OnEndpointAdded(const WiFiEndpointConstRefPtr &endpoint);
+
+  // Called by a Device when it removes an Endpoint.  If the Provider
+  // forgets a service as a result, it returns a reference to the
+  // forgotten service, otherwise it returns a null reference.
+  virtual WiFiServiceRefPtr OnEndpointRemoved(
+      const WiFiEndpointConstRefPtr &endpoint);
+
+  // Called by a WiFiService when it is unloaded and no longer visible.
+  virtual bool OnServiceUnloaded(const WiFiServiceRefPtr &service);
+
+  // Get the list of SSIDs for hidden WiFi services we are aware of.
+  virtual ByteArrays GetHiddenSSIDList();
+
+  // Calls WiFiService::FixupServiceEntries() and adds a UMA metric if
+  // this causes entries to be updated.
+  virtual void FixupServiceEntries(
+      StoreInterface *storage, bool is_default_profile);
 
  private:
   friend class WiFiProviderTest;
 
+  static const char kManagerErrorSSIDTooLong[];
+  static const char kManagerErrorSSIDTooShort[];
+  static const char kManagerErrorSSIDRequired[];
+  static const char kManagerErrorUnsupportedSecurityMode[];
+  static const char kManagerErrorUnsupportedServiceMode[];
+
+  // Add a service to the service_ vector and register it with the Manager.
+  WiFiServiceRefPtr AddService(const std::vector<uint8_t> &ssid,
+                               const std::string &mode,
+                               const std::string &security,
+                               bool is_hidden);
+
+  // Find a service given its properties.
+  WiFiServiceRefPtr FindService(const std::vector<uint8_t> &ssid,
+                                const std::string &mode,
+                                const std::string &security) const;
+
+  // Disassociate the service from its WiFi device and remove it from the
+  // services_ vector.
+  void ForgetService(const WiFiServiceRefPtr &service);
+
   ControlInterface *control_interface_;
   EventDispatcher *dispatcher_;
   Metrics *metrics_;
   Manager *manager_;
 
+  std::vector<WiFiServiceRefPtr> services_;
+
   DISALLOW_COPY_AND_ASSIGN(WiFiProvider);
 };
 
diff --git a/wifi_provider_unittest.cc b/wifi_provider_unittest.cc
index 21b9fd8..5b14adb 100644
--- a/wifi_provider_unittest.cc
+++ b/wifi_provider_unittest.cc
@@ -4,11 +4,37 @@
 
 #include "shill/wifi_provider.h"
 
+#include <base/string_number_conversions.h>
+#include <base/string_util.h>
+#include <base/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include <set>
+#include <string>
+#include <vector>
+
+#include "shill/ieee80211.h"
+#include "shill/mock_event_dispatcher.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_metrics.h"
+#include "shill/mock_profile.h"
+#include "shill/mock_store.h"
+#include "shill/mock_wifi_service.h"
 #include "shill/nice_mock_control.h"
+#include "shill/technology.h"
+#include "shill/wifi_endpoint.h"
+#include "shill/wpa_supplicant.h"
+
+using std::set;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrictMock;
 
 namespace shill {
 
@@ -16,19 +42,812 @@
  public:
   WiFiProviderTest()
       : metrics_(NULL),
-        manager_(&control_, NULL, &metrics_, NULL),
-        provider_(&control_, NULL, &metrics_, &manager_) {}
+        manager_(&control_, &dispatcher_, &metrics_,
+                 reinterpret_cast<GLib *>(NULL)),
+        provider_(&control_, &dispatcher_, &metrics_, &manager_),
+        profile_(new NiceMock<MockProfile>(&control_, &manager_, "")),
+        storage_entry_index_(0) {}
 
   virtual ~WiFiProviderTest() {}
 
+  virtual void SetUp() {
+    EXPECT_CALL(*profile_, GetConstStorage()).WillRepeatedly(Return(&storage_));
+  }
+
  protected:
+  typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;
+
+  void CreateServicesFromProfile() {
+    provider_.CreateServicesFromProfile(profile_);
+  }
+
+  void FixupServiceEntries(bool is_default_profile) {
+    provider_.FixupServiceEntries(&storage_, is_default_profile);
+  }
+
+  const vector<WiFiServiceRefPtr> GetServices() {
+    return provider_.services_;
+  }
+
+  void AddStringParameterToStorage(const string &id,
+                                   const string &key,
+                                   const string &value) {
+    EXPECT_CALL(storage_, GetString(id, key, _))
+        .WillRepeatedly(DoAll(SetArgumentPointee<2>(value),
+                              Return(true)));
+  }
+
+  string AddServiceToStorage(const char *ssid,
+                             const char *mode,
+                             const char *security,
+                             bool is_hidden,
+                             bool provide_hidden) {
+    string id = StringToLowerASCII(base::StringPrintf("entry_%d",
+                                                      storage_entry_index_));
+    EXPECT_CALL(storage_, GetString(id, _, _)).WillRepeatedly(Return(false));
+    if (ssid) {
+      const string ssid_string(ssid);
+      const string hex_ssid(
+          base::HexEncode(ssid_string.data(), ssid_string.size()));
+      AddStringParameterToStorage(id, WiFiService::kStorageSSID, hex_ssid);
+    }
+    if (mode) {
+      AddStringParameterToStorage(id, WiFiService::kStorageMode, mode);
+    }
+    if (security) {
+      AddStringParameterToStorage(id, WiFiService::kStorageSecurity, security);
+    }
+    if (provide_hidden) {
+      EXPECT_CALL(storage_, GetBool(id, flimflam::kWifiHiddenSsid, _))
+          .WillRepeatedly(
+              DoAll(SetArgumentPointee<2>(is_hidden), Return(true)));
+    } else {
+      EXPECT_CALL(storage_, GetBool(id, flimflam::kWifiHiddenSsid, _))
+          .WillRepeatedly(Return(false));
+    }
+    storage_entry_index_++;
+    return id;
+  }
+
+  WiFiServiceRefPtr GetService(const char *ssid,
+                               const char *mode,
+                               const char *security,
+                               bool is_hidden,
+                               bool provide_hidden,
+                               Error *error) {
+    KeyValueStore args;
+    args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
+    if (ssid) {
+      args.SetString(flimflam::kSSIDProperty, ssid);
+    }
+    if (mode) {
+      args.SetString(flimflam::kModeProperty, mode);
+    }
+    if (security) {
+      args.SetString(flimflam::kSecurityProperty, security);
+    }
+    if (provide_hidden) {
+      args.SetBool(flimflam::kWifiHiddenSsid, is_hidden);
+    }
+    return provider_.GetService(args, error);
+  }
+
+  WiFiServiceRefPtr FindService(const vector<uint8_t> &ssid,
+                                const string &mode,
+                                const string &security) {
+    return provider_.FindService(ssid, mode, security);
+  }
+  WiFiEndpointRefPtr MakeEndpoint(const string &ssid, const string &bssid,
+                                  uint16 frequency, int16 signal_dbm) {
+    return WiFiEndpoint::MakeOpenEndpoint(
+        NULL, NULL, ssid, bssid, wpa_supplicant::kNetworkModeInfrastructure,
+        frequency, signal_dbm);
+  }
+  MockWiFiServiceRefPtr AddMockService(const vector<uint8_t> &ssid,
+                                       const string &mode,
+                                       const string &security,
+                                       bool hidden_ssid) {
+    MockWiFiServiceRefPtr service = new MockWiFiService(
+        &control_,
+        &dispatcher_,
+        &metrics_,
+        &manager_,
+        &provider_,
+        ssid,
+        mode,
+        security,
+        hidden_ssid);
+    provider_.services_.push_back(service);
+    return service;
+  }
   NiceMockControl control_;
+  MockEventDispatcher dispatcher_;
   MockMetrics metrics_;
-  MockManager manager_;
+  StrictMock<MockManager> manager_;
   WiFiProvider provider_;
+  scoped_refptr<MockProfile> profile_;
+  StrictMock<MockStore> storage_;
+  int storage_entry_index_;
 };
 
-TEST_F(WiFiProviderTest, Constructor) {
+MATCHER(TypeWiFiPropertyMatch, "") {
+  return
+      arg.bool_properties().empty() &&
+      arg.int_properties().empty() &&
+      arg.uint_properties().empty() &&
+      arg.string_properties().size() == 1 &&
+      arg.LookupString(flimflam::kTypeProperty, "") == flimflam::kTypeWifi;
+}
+
+MATCHER_P(RefPtrMatch, ref, "") {
+  return ref.get() == arg.get();
+}
+
+TEST_F(WiFiProviderTest, Start) {
+  // Doesn't do anything really.  Just testing for no crash.
+  EXPECT_TRUE(GetServices().empty());
+  provider_.Start();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, Stop) {
+  MockWiFiServiceRefPtr service0 = AddMockService(vector<uint8_t>(1, '0'),
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  MockWiFiServiceRefPtr service1 = AddMockService(vector<uint8_t>(1, '1'),
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(2, GetServices().size());
+  EXPECT_CALL(*service0, ResetWiFi()).Times(1);
+  EXPECT_CALL(*service1, ResetWiFi()).Times(1);
+  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service0))).Times(1);
+  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service1))).Times(1);
+  provider_.Stop();
+  // Verify now, so it's clear that this happened as a result of the call
+  // above, and not anything in the destructor(s).
+  Mock::VerifyAndClearExpectations(service0);
+  Mock::VerifyAndClearExpectations(service1);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileWithNoGroups) {
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillOnce(Return(set<string>()));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingSSID) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      NULL, flimflam::kModeManaged, flimflam::kSecurityNone, false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptySSID) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "", flimflam::kModeManaged, flimflam::kSecurityNone, false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingMode) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", NULL, flimflam::kSecurityNone, false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptyMode) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", "", flimflam::kSecurityNone, false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingSecurity) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", flimflam::kModeManaged, NULL, false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptySecurity) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", flimflam::kModeManaged, "", false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingHidden) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", flimflam::kModeManaged, flimflam::kSecurityNone, false, false));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  CreateServicesFromProfile();
+  EXPECT_TRUE(GetServices().empty());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileSingle) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  string kSSID("foo");
+  groups.insert(AddServiceToStorage(
+      kSSID.c_str(), flimflam::kModeManaged, flimflam::kSecurityNone,
+      false, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  CreateServicesFromProfile();
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_EQ(1, GetServices().size());
+
+  const WiFiServiceRefPtr service = GetServices().front();
+  const string service_ssid(service->ssid().begin(), service->ssid().end());
+  EXPECT_EQ(kSSID, service_ssid);
+  EXPECT_EQ(flimflam::kModeManaged, service->mode());
+  EXPECT_TRUE(service->IsSecurityMatch(flimflam::kSecurityNone));
+
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  CreateServicesFromProfile();
+  EXPECT_EQ(1, GetServices().size());
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileHiddenButConnected) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  string kSSID("foo");
+  groups.insert(AddServiceToStorage(
+      kSSID.c_str(), flimflam::kModeManaged, flimflam::kSecurityNone,
+      true, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
+      .WillOnce(Return(true));
+  EXPECT_CALL(manager_, RequestScan(_, _)).Times(0);
+  CreateServicesFromProfile();
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, IsTechnologyConnected(_)).Times(0);
+  CreateServicesFromProfile();
+}
+
+TEST_F(WiFiProviderTest, CreateServicesFromProfileHiddenNotConnected) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  string kSSID("foo");
+  groups.insert(AddServiceToStorage(
+      kSSID.c_str(), flimflam::kModeManaged, flimflam::kSecurityNone,
+      true, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
+      .WillOnce(Return(false));
+  EXPECT_CALL(manager_, RequestScan(flimflam::kTypeWifi, _)).Times(1);
+  CreateServicesFromProfile();
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, IsTechnologyConnected(_)).Times(0);
+  EXPECT_CALL(manager_, RequestScan(_, _)).Times(0);
+  CreateServicesFromProfile();
+}
+
+TEST_F(WiFiProviderTest, CreateTwoServices) {
+  string id;
+  StrictMock<MockStore> storage;
+  set<string> groups;
+  groups.insert(AddServiceToStorage(
+      "foo", flimflam::kModeManaged, flimflam::kSecurityNone, false, true));
+  groups.insert(AddServiceToStorage(
+      "bar", flimflam::kModeManaged, flimflam::kSecurityNone, true, true));
+  EXPECT_CALL(storage_, GetGroupsWithProperties(TypeWiFiPropertyMatch()))
+      .WillRepeatedly(Return(groups));
+  EXPECT_CALL(manager_, RegisterService(_)).Times(2);
+  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
+      .WillOnce(Return(true));
+  EXPECT_CALL(manager_, RequestScan(flimflam::kTypeWifi, _)).Times(0);
+  CreateServicesFromProfile();
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  EXPECT_EQ(2, GetServices().size());
+}
+
+TEST_F(WiFiProviderTest, GetServiceNoMode) {
+  Error error;
+  EXPECT_FALSE(GetService("foo", "", flimflam::kSecurityNone,
+                          false, false, &error).get());
+  EXPECT_EQ(Error::kNotSupported, error.type());
+}
+
+TEST_F(WiFiProviderTest, GetServiceBadMode) {
+  Error error;
+  EXPECT_FALSE(GetService("foo", flimflam::kModeAdhoc,
+                          flimflam::kSecurityNone,
+                          false, false, &error).get());
+  EXPECT_EQ(Error::kNotSupported, error.type());
+  EXPECT_EQ("service mode is unsupported", error.message());
+}
+
+TEST_F(WiFiProviderTest, GetServiceNoSSID) {
+  Error error;
+  EXPECT_FALSE(GetService(NULL, flimflam::kModeManaged,
+                          flimflam::kSecurityNone, false, false,
+                          &error).get());
+  EXPECT_EQ(Error::kInvalidArguments, error.type());
+  EXPECT_EQ("must specify SSID", error.message());
+}
+
+TEST_F(WiFiProviderTest, GetServiceEmptySSID) {
+  Error error;
+  EXPECT_FALSE(GetService("", flimflam::kModeManaged,
+                          flimflam::kSecurityNone, false, false,
+                          &error).get());
+  EXPECT_EQ(Error::kInvalidNetworkName, error.type());
+  EXPECT_EQ("SSID is too short", error.message());
+}
+
+TEST_F(WiFiProviderTest, GetServiceLongSSID) {
+  Error error;
+  string ssid(IEEE_80211::kMaxSSIDLen + 1, '0');
+  EXPECT_FALSE(GetService(ssid.c_str(), flimflam::kModeManaged,
+                          flimflam::kSecurityNone, false, false,
+                          &error).get());
+  EXPECT_EQ(Error::kInvalidNetworkName, error.type());
+  EXPECT_EQ("SSID is too long", error.message());
+}
+
+TEST_F(WiFiProviderTest, GetServiceJustLongEnoughSSID) {
+  Error error;
+  string ssid(IEEE_80211::kMaxSSIDLen, '0');
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  EXPECT_TRUE(GetService(ssid.c_str(), flimflam::kModeManaged,
+                         flimflam::kSecurityNone, false, false,
+                         &error));
+  EXPECT_TRUE(error.IsSuccess());
+}
+
+TEST_F(WiFiProviderTest, GetServiceBadSecurty) {
+  Error error;
+  EXPECT_FALSE(GetService("foo", flimflam::kModeManaged,
+                          "pig-80211", false, false,
+                          &error));
+  EXPECT_EQ(Error::kNotSupported, error.type());
+  EXPECT_EQ("security mode is unsupported", error.message());
+}
+
+TEST_F(WiFiProviderTest, GetServiceMinimal) {
+  Error error;
+  const string kSSID("foo");
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  WiFiServiceRefPtr service = GetService(kSSID.c_str(), flimflam::kModeManaged,
+                                         NULL, false, false, &error);
+  EXPECT_TRUE(service.get());
+  EXPECT_TRUE(error.IsSuccess());
+  const string service_ssid(service->ssid().begin(), service->ssid().end());
+  EXPECT_EQ(kSSID, service_ssid);
+  EXPECT_EQ(flimflam::kModeManaged, service->mode());
+
+  // These two should be set to their default values if not specified.
+  EXPECT_TRUE(service->IsSecurityMatch(flimflam::kSecurityNone));
+  EXPECT_TRUE(service->hidden_ssid());
+}
+
+TEST_F(WiFiProviderTest, GetServiceFullySpecified) {
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  const string kSSID("bar");
+  Error error;
+  WiFiServiceRefPtr service0 =
+      GetService(kSSID.c_str(), flimflam::kModeManaged,
+                 flimflam::kSecurityPsk, false, true, &error);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_TRUE(error.IsSuccess());
+  const string service_ssid(service0->ssid().begin(), service0->ssid().end());
+  EXPECT_EQ(kSSID, service_ssid);
+  EXPECT_EQ(flimflam::kModeManaged, service0->mode());
+  EXPECT_TRUE(service0->IsSecurityMatch(flimflam::kSecurityPsk));
+  EXPECT_FALSE(service0->hidden_ssid());
+
+  // Getting the same service parameters (even with a different hidden
+  // parameter) should return the same service.
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  WiFiServiceRefPtr service1 =
+      GetService(kSSID.c_str(), flimflam::kModeManaged,
+                 flimflam::kSecurityPsk, true, true, &error);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_EQ(service0.get(), service1.get());
+  EXPECT_EQ(1, GetServices().size());
+
+  // Getting the same ssid with different other parameters should return
+  // a different service.
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  WiFiServiceRefPtr service2 =
+      GetService(kSSID.c_str(), flimflam::kModeManaged,
+                 flimflam::kSecurityNone, true, true, &error);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_NE(service0.get(), service2.get());
+  EXPECT_EQ(2, GetServices().size());
+}
+
+TEST_F(WiFiProviderTest, FindServiceWPA) {
+  const string kSSID("an_ssid");
+  Error error;
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  WiFiServiceRefPtr service = GetService(
+      kSSID.c_str(), flimflam::kModeManaged, flimflam::kSecurityRsn,
+       false, true, &error);
+  ASSERT_TRUE(service);
+  const vector<uint8_t> ssid_bytes(kSSID.begin(), kSSID.end());
+  WiFiServiceRefPtr wpa_service(FindService(ssid_bytes, flimflam::kModeManaged,
+                                            flimflam::kSecurityWpa));
+  EXPECT_TRUE(wpa_service);
+  EXPECT_EQ(service.get(), wpa_service.get());
+  WiFiServiceRefPtr rsn_service(FindService(ssid_bytes, flimflam::kModeManaged,
+                                            flimflam::kSecurityRsn));
+  EXPECT_TRUE(rsn_service.get());
+  EXPECT_EQ(service.get(), rsn_service.get());
+  WiFiServiceRefPtr psk_service(FindService(ssid_bytes, flimflam::kModeManaged,
+                                            flimflam::kSecurityPsk));
+  EXPECT_EQ(service.get(), psk_service.get());
+  WiFiServiceRefPtr wep_service(FindService(ssid_bytes, flimflam::kModeManaged,
+                                            flimflam::kSecurityWep));
+  EXPECT_TRUE(service.get() != wep_service.get());
+  EXPECT_EQ(NULL, wep_service.get());
+}
+
+TEST_F(WiFiProviderTest, FindServiceForEndpoint) {
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  Error error;
+  const string kSSID("an_ssid");
+  WiFiServiceRefPtr service = GetService(
+      kSSID.c_str(), flimflam::kModeManaged, flimflam::kSecurityNone,
+       false, true, &error);
+  ASSERT_TRUE(service);
+  WiFiEndpointRefPtr endpoint = MakeEndpoint(kSSID, "00:00:00:00:00:00", 0, 0);
+  WiFiServiceRefPtr endpoint_service =
+      provider_.FindServiceForEndpoint(endpoint);
+  EXPECT_EQ(service.get(), endpoint_service.get());
+}
+
+TEST_F(WiFiProviderTest, OnEndpointAdded) {
+  const string ssid0("an_ssid");
+  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
+  EXPECT_FALSE(FindService(ssid0_bytes, flimflam::kModeManaged,
+                           flimflam::kSecurityNone));
+  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
+  provider_.OnEndpointAdded(endpoint0);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_EQ(1, GetServices().size());
+  WiFiServiceRefPtr service0(FindService(ssid0_bytes, flimflam::kModeManaged,
+                                         flimflam::kSecurityNone));
+  EXPECT_TRUE(service0);
+  EXPECT_TRUE(service0->HasEndpoints());
+
+  WiFiEndpointRefPtr endpoint1 = MakeEndpoint(ssid0, "00:00:00:00:00:01", 0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
+  provider_.OnEndpointAdded(endpoint1);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_EQ(1, GetServices().size());
+
+  const string ssid1("another_ssid");
+  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
+  EXPECT_FALSE(FindService(ssid1_bytes, flimflam::kModeManaged,
+                           flimflam::kSecurityNone));
+  WiFiEndpointRefPtr endpoint2 = MakeEndpoint(ssid1, "00:00:00:00:00:02",
+                                              0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
+  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
+  provider_.OnEndpointAdded(endpoint2);
+  Mock::VerifyAndClearExpectations(&manager_);
+  EXPECT_EQ(2, GetServices().size());
+
+  WiFiServiceRefPtr service1(FindService(ssid1_bytes, flimflam::kModeManaged,
+                                         flimflam::kSecurityNone));
+  EXPECT_TRUE(service1);
+  EXPECT_TRUE(service1->HasEndpoints());
+  EXPECT_TRUE(service1 != service0);
+}
+
+TEST_F(WiFiProviderTest, OnEndpointAddedToMockService) {
+  // The previous test allowed the provider to create its own "real"
+  // WiFiServices, which hides some of what we can test with mock
+  // services.  Re-do an add-endpoint operation by seeding the provider
+  // with a mock service.
+  const string ssid0("an_ssid");
+  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
+  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  const string ssid1("another_ssid");
+  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
+  MockWiFiServiceRefPtr service1 = AddMockService(ssid1_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(service0.get(), FindService(ssid0_bytes,
+                                        flimflam::kModeManaged,
+                                        flimflam::kSecurityNone).get());
+  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
+  EXPECT_CALL(*service0, AddEndpoint(RefPtrMatch(endpoint0))).Times(1);
+  EXPECT_CALL(*service1, AddEndpoint(_)).Times(0);
+  provider_.OnEndpointAdded(endpoint0);
+  Mock::VerifyAndClearExpectations(&manager_);
+  Mock::VerifyAndClearExpectations(service0);
+  Mock::VerifyAndClearExpectations(service1);
+
+  WiFiEndpointRefPtr endpoint1 = MakeEndpoint(ssid0, "00:00:00:00:00:01", 0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
+  EXPECT_CALL(*service0, AddEndpoint(RefPtrMatch(endpoint1))).Times(1);
+  EXPECT_CALL(*service1, AddEndpoint(_)).Times(0);
+  provider_.OnEndpointAdded(endpoint1);
+  Mock::VerifyAndClearExpectations(&manager_);
+  Mock::VerifyAndClearExpectations(service0);
+  Mock::VerifyAndClearExpectations(service1);
+
+  WiFiEndpointRefPtr endpoint2 = MakeEndpoint(ssid1, "00:00:00:00:00:02", 0, 0);
+  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service1))).Times(1);
+  EXPECT_CALL(*service0, AddEndpoint(_)).Times(0);
+  EXPECT_CALL(*service1, AddEndpoint(RefPtrMatch(endpoint2))).Times(1);
+  provider_.OnEndpointAdded(endpoint2);
+}
+
+TEST_F(WiFiProviderTest, OnEndpointRemoved) {
+  const string ssid0("an_ssid");
+  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
+  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  const string ssid1("another_ssid");
+  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
+  MockWiFiServiceRefPtr service1 = AddMockService(ssid1_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(2, GetServices().size());
+
+  // Remove the last endpoint of a non-remembered service.
+  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
+  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
+  EXPECT_CALL(*service1, RemoveEndpoint(_)).Times(0);
+  EXPECT_CALL(*service0, HasEndpoints()).WillOnce(Return(false));
+  EXPECT_CALL(*service0, IsRemembered()).WillOnce(Return(false));
+  EXPECT_CALL(*service0, ResetWiFi()).Times(1);
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(0);
+  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service0))).Times(1);
+  provider_.OnEndpointRemoved(endpoint0);
+  // Verify now, so it's clear that this happened as a result of the call
+  // above, and not anything in the destructor(s).
+  Mock::VerifyAndClearExpectations(&manager_);
+  Mock::VerifyAndClearExpectations(service0);
+  Mock::VerifyAndClearExpectations(service1);
+  EXPECT_EQ(1, GetServices().size());
+  EXPECT_EQ(service1.get(), GetServices().front().get());
+}
+
+TEST_F(WiFiProviderTest, OnEndpointRemovedButHasEndpoints) {
+  const string ssid0("an_ssid");
+  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
+  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(1, GetServices().size());
+
+  // Remove an endpoint of a non-remembered service.
+  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
+  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
+  EXPECT_CALL(*service0, HasEndpoints()).WillOnce(Return(true));
+  EXPECT_CALL(*service0, IsRemembered()).WillRepeatedly(Return(false));
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
+  EXPECT_CALL(*service0, ResetWiFi()).Times(0);
+  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);
+  provider_.OnEndpointRemoved(endpoint0);
+  // Verify now, so it's clear that this happened as a result of the call
+  // above, and not anything in the destructor(s).
+  Mock::VerifyAndClearExpectations(&manager_);
+  Mock::VerifyAndClearExpectations(service0);
+  EXPECT_EQ(1, GetServices().size());
+}
+
+TEST_F(WiFiProviderTest, OnEndpointRemovedButIsRemembered) {
+  const string ssid0("an_ssid");
+  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
+  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(1, GetServices().size());
+
+  // Remove the last endpoint of a remembered service.
+  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
+  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
+  EXPECT_CALL(*service0, HasEndpoints()).WillRepeatedly(Return(false));
+  EXPECT_CALL(*service0, IsRemembered()).WillOnce(Return(true));
+  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
+  EXPECT_CALL(*service0, ResetWiFi()).Times(0);
+  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);
+  provider_.OnEndpointRemoved(endpoint0);
+  // Verify now, so it's clear that this happened as a result of the call
+  // above, and not anything in the destructor(s).
+  Mock::VerifyAndClearExpectations(&manager_);
+  Mock::VerifyAndClearExpectations(service0);
+  EXPECT_EQ(1, GetServices().size());
+}
+
+TEST_F(WiFiProviderTest, OnServiceUnloaded) {
+  // This function should never unregister services itself -- the Manager
+  // will automatically deregister the service if OnServiceUnloaded()
+  // returns true (via WiFiService::Unload()).
+  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);
+
+  MockWiFiServiceRefPtr service = AddMockService(vector<uint8_t>(1, '0'),
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_EQ(1, GetServices().size());
+  EXPECT_CALL(*service, HasEndpoints()).WillOnce(Return(true));
+  EXPECT_CALL(*service, ResetWiFi()).Times(0);
+  EXPECT_FALSE(provider_.OnServiceUnloaded(service));
+  EXPECT_EQ(1, GetServices().size());
+  Mock::VerifyAndClearExpectations(service);
+
+  EXPECT_CALL(*service, HasEndpoints()).WillOnce(Return(false));
+  EXPECT_CALL(*service, ResetWiFi()).Times(1);
+  EXPECT_TRUE(provider_.OnServiceUnloaded(service));
+  // Verify now, so it's clear that this happened as a result of the call
+  // above, and not anything in the destructor(s).
+  Mock::VerifyAndClearExpectations(service);
+  EXPECT_TRUE(GetServices().empty());
+
+  Mock::VerifyAndClearExpectations(&manager_);
+}
+
+TEST_F(WiFiProviderTest, FixupServiceEntries) {
+  // We test FixupServiceEntries indirectly since it calls a static method
+  // in WiFiService.
+  EXPECT_CALL(metrics_, SendEnumToUMA(
+      "Network.Shill.Wifi.ServiceFixupEntries",
+      Metrics::kMetricServiceFixupDefaultProfile,
+      Metrics::kMetricServiceFixupMax)).Times(1);
+  EXPECT_CALL(storage_, Flush()).Times(1);
+  const string kGroupId =
+      StringPrintf("%s_0_0_%s_%s",
+                   flimflam::kTypeWifi,
+                   flimflam::kModeManaged,
+                   flimflam::kSecurityNone);
+  EXPECT_CALL(storage_,
+              GetString(kGroupId, _, _)).WillRepeatedly(Return(false));
+  EXPECT_CALL(storage_,
+              SetString(kGroupId, _, _)).WillRepeatedly(Return(true));
+  set<string> groups;
+  groups.insert(kGroupId);
+  EXPECT_CALL(storage_, GetGroups()).WillRepeatedly(Return(groups));
+  FixupServiceEntries(true);
+  Mock::VerifyAndClearExpectations(&metrics_);
+
+  EXPECT_CALL(metrics_, SendEnumToUMA(
+      "Network.Shill.Wifi.ServiceFixupEntries",
+      Metrics::kMetricServiceFixupUserProfile,
+      Metrics::kMetricServiceFixupMax)).Times(1);
+  EXPECT_CALL(storage_, Flush()).Times(1);
+  FixupServiceEntries(false);
+}
+
+TEST_F(WiFiProviderTest, FixupServiceEntriesNothingToDo) {
+  EXPECT_CALL(metrics_, SendEnumToUMA(_, _, _)).Times(0);
+  EXPECT_CALL(storage_, Flush()).Times(0);
+  const string kGroupId =
+      StringPrintf("%s_0_0_%s_%s",
+                   flimflam::kTypeWifi,
+                   flimflam::kModeManaged,
+                   flimflam::kSecurityNone);
+  EXPECT_CALL(storage_,
+              GetString(kGroupId, _, _)).WillRepeatedly(Return(true));
+  set<string> groups;
+  groups.insert(kGroupId);
+  EXPECT_CALL(storage_, GetGroups()).WillOnce(Return(groups));
+  FixupServiceEntries(true);
+}
+
+TEST_F(WiFiProviderTest, GetHiddenSSIDList) {
+  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());
+  const vector<uint8_t> ssid0(1, '0');
+  AddMockService(ssid0, flimflam::kModeManaged,
+                 flimflam::kSecurityNone, false);
+  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());
+
+  const vector<uint8_t> ssid1(1, '1');
+  MockWiFiServiceRefPtr service1 = AddMockService(ssid1,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  true);
+  EXPECT_CALL(*service1, IsRemembered()).WillRepeatedly(Return(false));
+  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());
+
+  const vector<uint8_t> ssid2(1, '2');
+  MockWiFiServiceRefPtr service2 = AddMockService(ssid2,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  true);
+  EXPECT_CALL(*service2, IsRemembered()).WillRepeatedly(Return(true));
+  ByteArrays ssid_list = provider_.GetHiddenSSIDList();
+
+  EXPECT_EQ(1, ssid_list.size());
+  EXPECT_TRUE(ssid_list[0] == ssid2);
+
+  const vector<uint8_t> ssid3(1, '3');
+  MockWiFiServiceRefPtr service3 = AddMockService(ssid3,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  false);
+  EXPECT_CALL(*service2, IsRemembered()).WillRepeatedly(Return(true));
+
+  ssid_list = provider_.GetHiddenSSIDList();
+  EXPECT_EQ(1, ssid_list.size());
+  EXPECT_TRUE(ssid_list[0] == ssid2);
+
+  const vector<uint8_t> ssid4(1, '4');
+  MockWiFiServiceRefPtr service4 = AddMockService(ssid4,
+                                                  flimflam::kModeManaged,
+                                                  flimflam::kSecurityNone,
+                                                  true);
+  EXPECT_CALL(*service4, IsRemembered()).WillRepeatedly(Return(true));
+
+  ssid_list = provider_.GetHiddenSSIDList();
+  EXPECT_EQ(2, ssid_list.size());
+  EXPECT_TRUE(ssid_list[0] == ssid2);
+  EXPECT_TRUE(ssid_list[1] == ssid4);
 }
 
 }  // namespace shill
diff --git a/wifi_service.cc b/wifi_service.cc
index 1c2a01b..768e0e1 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -28,6 +28,7 @@
 #include "shill/store_interface.h"
 #include "shill/wifi.h"
 #include "shill/wifi_endpoint.h"
+#include "shill/wifi_provider.h"
 #include "shill/wpa_supplicant.h"
 
 using std::set;
@@ -37,6 +38,7 @@
 namespace shill {
 
 const char WiFiService::kAutoConnNoEndpoint[] = "no endpoints";
+const char WiFiService::kAnyDeviceAddress[] = "any";
 
 const char WiFiService::kStorageHiddenSSID[] = "WiFi.HiddenSSID";
 const char WiFiService::kStorageMode[] = "WiFi.Mode";
@@ -50,7 +52,7 @@
                          EventDispatcher *dispatcher,
                          Metrics *metrics,
                          Manager *manager,
-                         const WiFiRefPtr &device,
+                         WiFiProvider *provider,
                          const vector<uint8_t> &ssid,
                          const string &mode,
                          const string &security,
@@ -64,10 +66,10 @@
       frequency_(0),
       physical_mode_(0),
       raw_signal_strength_(0),
-      wifi_(device),
       ssid_(ssid),
       ieee80211w_required_(false),
-      nss_(NSS::GetInstance()) {
+      nss_(NSS::GetInstance()),
+      provider_(provider) {
   PropertyStore *store = this->mutable_store();
   store->RegisterConstString(flimflam::kModeProperty, &mode_);
   HelpRegisterWriteOnlyDerivedString(flimflam::kPassphraseProperty,
@@ -145,6 +147,8 @@
     return false;
   }
 
+  CHECK(wifi_) << "We have endpoints but no WiFi device is selected?";
+
   // Do not preempt an existing connection (whether pending, or
   // connected, and whether to this service, or another).
   if (!wifi_->IsIdle()) {
@@ -177,14 +181,16 @@
   UpdateFromEndpoints();
 }
 
-void WiFiService::NotifyCurrentEndpoint(const WiFiEndpoint *endpoint) {
+void WiFiService::NotifyCurrentEndpoint(
+    const WiFiEndpointConstRefPtr &endpoint) {
   DCHECK(!endpoint || (endpoints_.find(endpoint) != endpoints_.end()));
   current_endpoint_ = endpoint;
   UpdateFromEndpoints();
 }
 
-void WiFiService::NotifyEndpointUpdated(const WiFiEndpoint &endpoint) {
-  DCHECK(endpoints_.find(&endpoint) != endpoints_.end());
+void WiFiService::NotifyEndpointUpdated(
+    const WiFiEndpointConstRefPtr &endpoint) {
+  DCHECK(endpoints_.find(endpoint) != endpoints_.end());
   UpdateFromEndpoints();
 }
 
@@ -298,7 +304,7 @@
   hidden_ssid_ = false;
   Error unused_error;
   ClearPassphrase(&unused_error);
-  return !IsVisible();
+  return provider_->OnServiceUnloaded(this);
 }
 
 bool WiFiService::IsSecurityMatch(const string &security) const {
@@ -395,10 +401,29 @@
                           Error::GetDefaultMessage(Error::kAlreadyConnected));
     return;
   }
-  if (wifi_->IsCurrentService(this)) {
+
+  WiFiRefPtr wifi = wifi_;
+  if (!wifi) {
+    // If this is a hidden service before it has been found in a scan, we
+    // may need to late-bind to any available WiFi Device.  We don't actually
+    // set |wifi_| in this case snce we do not yet see any endpoints.  This
+    // will mean this service is not disconnectable until an endpoint is
+    // found.
+    wifi = ChooseDevice();
+    if (!wifi) {
+      LOG(ERROR) << "Can't connect. Service " << unique_name()
+                 << " cannot find a WiFi device.";
+      Error::PopulateAndLog(error,
+                            Error::kOperationFailed,
+                            Error::GetDefaultMessage(Error::kOperationFailed));
+      return;
+    }
+  }
+
+  if (wifi->IsCurrentService(this)) {
     LOG(WARNING) << "Can't connect.  Service " << unique_name()
                  << " is the current service (but, in " << GetStateString()
-                 << " state, not connected.";
+                 << " state, not connected).";
     Error::PopulateAndLog(error,
                           Error::kInProgress,
                           Error::GetDefaultMessage(Error::kInProgress));
@@ -473,16 +498,33 @@
   writer = params[wpa_supplicant::kNetworkPropertySSID].writer();
   writer << ssid_;
 
-  wifi_->ConnectTo(this, params);
+  wifi->ConnectTo(this, params);
 }
 
 void WiFiService::Disconnect(Error *error) {
   LOG(INFO) << __func__;
   Service::Disconnect(error);
+  if (!wifi_) {
+    // If we are connecting to a hidden service, but have not yet found
+    // any endpoints, we could end up with a disconnect request without
+    // a wifi_ reference.  This is not a fatal error.
+    LOG_IF(ERROR, IsConnecting())
+         << "WiFi endpoints do not (yet) exist.  Cannot disconnect service "
+         << unique_name();
+    LOG_IF(FATAL, IsConnected())
+         << "WiFi device does not exist.  Cannot disconnect service "
+         << unique_name();
+    error->Populate(Error::kOperationFailed);
+    return;
+  }
   wifi_->DisconnectFrom(this);
 }
 
-string WiFiService::GetDeviceRpcId(Error */*error*/) {
+string WiFiService::GetDeviceRpcId(Error *error) {
+  if (!wifi_) {
+    error->Populate(Error::kNotFound, "Not associated with a device");
+    return "/";
+  }
   return wifi_->GetRpcIdentifier();
 }
 
@@ -520,6 +562,13 @@
     }
   }
 
+  WiFiRefPtr wifi;
+  if (representative_endpoint) {
+    wifi = representative_endpoint->device();
+  }
+
+  SetWiFi(wifi);
+
   for (set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.begin();
        i != endpoints_.end(); ++i) {
     if ((*i)->ieee80211w_required()) {
@@ -758,6 +807,16 @@
 }
 
 // static
+bool WiFiService::IsValidSecurityMethod(const string &method) {
+  return method == flimflam::kSecurityNone ||
+      method == flimflam::kSecurityWep ||
+      method == flimflam::kSecurityPsk ||
+      method == flimflam::kSecurityWpa ||
+      method == flimflam::kSecurityRsn ||
+      method == flimflam::kSecurity8021x;
+}
+
+// static
 uint8 WiFiService::SignalToStrength(int16 signal_dbm) {
   int16 strength;
   if (signal_dbm > 0) {
@@ -792,14 +851,16 @@
   string security = GetSecurityClass(security_);
   return StringToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s",
                                                flimflam::kTypeWifi,
-                                               wifi_->address().c_str(),
+                                               kAnyDeviceAddress,
                                                hex_ssid_.c_str(),
                                                mode_.c_str(),
                                                security.c_str()));
 }
 
 void WiFiService::ClearCachedCredentials() {
-  wifi_->ClearCachedCredentials(this);
+  if (wifi_) {
+    wifi_->ClearCachedCredentials(this);
+  }
 }
 
 void WiFiService::set_eap(const EapCredentials &new_eap) {
@@ -908,4 +969,26 @@
   }
 }
 
+WiFiRefPtr WiFiService::ChooseDevice() {
+  // TODO(pstew): Style frowns on dynamic_cast.  crosbug.com/38237
+  DeviceRefPtr device =
+      manager()->GetEnabledDeviceWithTechnology(Technology::kWifi);
+  return dynamic_cast<WiFi *>(device.get());
+}
+
+void WiFiService::ResetWiFi() {
+  SetWiFi(NULL);
+}
+
+void WiFiService::SetWiFi(const WiFiRefPtr &wifi) {
+  if (wifi_ == wifi) {
+    return;
+  }
+  ClearCachedCredentials();
+  if (wifi_) {
+    wifi_->DisassociateFromService(this);
+  }
+  wifi_ = wifi;
+}
+
 }  // namespace shill
diff --git a/wifi_service.h b/wifi_service.h
index a039f98..44f522f 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -23,6 +23,7 @@
 class Manager;
 class Metrics;
 class NSS;
+class WiFiProvider;
 
 class WiFiService : public Service {
  public:
@@ -39,7 +40,7 @@
               EventDispatcher *dispatcher,
               Metrics *metrics,
               Manager *manager,
-              const WiFiRefPtr &device,
+              WiFiProvider *provider,
               const std::vector<uint8_t> &ssid,
               const std::string &mode,
               const std::string &security,
@@ -51,17 +52,17 @@
   virtual void Disconnect(Error *error);
   virtual bool Is8021x() const;
 
-  void AddEndpoint(const WiFiEndpointConstRefPtr &endpoint);
-  void RemoveEndpoint(const WiFiEndpointConstRefPtr &endpoint);
-  int GetEndpointCount() const { return endpoints_.size(); }
+  virtual void AddEndpoint(const WiFiEndpointConstRefPtr &endpoint);
+  virtual void RemoveEndpoint(const WiFiEndpointConstRefPtr &endpoint);
+  virtual int GetEndpointCount() const { return endpoints_.size(); }
 
   // Called to update the identity of the currently connected endpoint.
   // To indicate that there is no currently connect endpoint, call with
   // |endpoint| set to NULL.
-  void NotifyCurrentEndpoint(const WiFiEndpoint *endpoint);
+  virtual void NotifyCurrentEndpoint(const WiFiEndpointConstRefPtr &endpoint);
   // Called to inform of changes in the properties of an endpoint.
   // (Not necessarily the currently connected endpoint.)
-  void NotifyEndpointUpdated(const WiFiEndpoint &endpoint);
+  virtual void NotifyEndpointUpdated(const WiFiEndpointConstRefPtr &endpoint);
 
   // wifi_<MAC>_<BSSID>_<mode_string>_<security_string>
   std::string GetStorageIdentifier() const;
@@ -75,6 +76,9 @@
   // these properties.  Returns true if any entries were fixed.
   static bool FixupServiceEntries(StoreInterface *storage);
 
+  // Validate |method| against all valid and supported security methods.
+  static bool IsValidSecurityMethod(const std::string &method);
+
   const std::string &mode() const { return mode_; }
   const std::string &key_management() const { return GetEAPKeyManagement(); }
   const std::vector<uint8_t> &ssid() const { return ssid_; }
@@ -108,6 +112,9 @@
   // have been configured.
   virtual void OnProfileConfigured();
 
+  // Called by WiFiProvider to reset the WiFi device reference on shutdown.
+  virtual void ResetWiFi();
+
  protected:
   virtual bool IsAutoConnectable(const char **reason) const;
 
@@ -137,6 +144,7 @@
   FRIEND_TEST(WiFiServiceTest, SignalToStrength);  // SignalToStrength
 
   static const char kAutoConnNoEndpoint[];
+  static const char kAnyDeviceAddress[];
 
   // Override the base clase implementation, because we need to allow
   // arguments that aren't base class methods.
@@ -186,6 +194,12 @@
   // Populate the |params| map with available 802.1x EAP properties.
   void Populate8021xProperties(std::map<std::string, DBus::Variant> *params);
 
+  // Select a WiFi device (e.g, for connecting a hidden service with no
+  // endpoints).
+  WiFiRefPtr ChooseDevice();
+
+  void SetWiFi(const WiFiRefPtr &wifi);
+
   // Properties
   std::string passphrase_;
   bool need_passphrase_;
@@ -217,6 +231,11 @@
   // mandated by one or more endpoints we have seen that provide this service.
   bool ieee80211w_required_;
   NSS *nss_;
+  // Bare pointer is safe because WiFi service instances are owned by
+  // the WiFiProvider and are guaranteed to be deallocated by the time
+  // the WiFiProvider is.
+  WiFiProvider *provider_;
+
   DISALLOW_COPY_AND_ASSIGN(WiFiService);
 };
 
diff --git a/wifi_service_unittest.cc b/wifi_service_unittest.cc
index d581cb6..59cecf3 100644
--- a/wifi_service_unittest.cc
+++ b/wifi_service_unittest.cc
@@ -26,6 +26,7 @@
 #include "shill/mock_service.h"
 #include "shill/mock_store.h"
 #include "shill/mock_wifi.h"
+#include "shill/mock_wifi_provider.h"
 #include "shill/property_store_unittest.h"
 #include "shill/refptr_types.h"
 #include "shill/wifi_endpoint.h"
@@ -39,6 +40,7 @@
 using ::testing::AnyNumber;
 using ::testing::DoAll;
 using ::testing::EndsWith;
+using ::testing::HasSubstr;
 using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::Return;
@@ -51,15 +53,17 @@
 
 class WiFiServiceTest : public PropertyStoreTest {
  public:
-  WiFiServiceTest() : wifi_(
-      new NiceMock<MockWiFi>(
-          control_interface(),
-          dispatcher(),
-          metrics(),
-          manager(),
-          "wifi",
-          fake_mac,
-          0)) {}
+  WiFiServiceTest()
+       : wifi_(new NiceMock<MockWiFi>(
+               control_interface(),
+               dispatcher(),
+               metrics(),
+               manager(),
+               "wifi",
+               fake_mac,
+               0)),
+         simple_ssid_(1, 'a'),
+         simple_ssid_string_("a") {}
   virtual ~WiFiServiceTest() {}
 
  protected:
@@ -68,16 +72,7 @@
   bool CheckConnectable(const std::string &security, const char *passphrase,
                         Service::EapCredentials *eap) {
     Error error;
-    vector<uint8_t> ssid(1, 'a');
-    WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                                dispatcher(),
-                                                metrics(),
-                                                manager(),
-                                                wifi(),
-                                                ssid,
-                                                flimflam::kModeManaged,
-                                                security,
-                                                false);
+    WiFiServiceRefPtr service = MakeSimpleService(security);
     if (passphrase)
       service->SetPassphrase(passphrase, &error);
     if (eap) {
@@ -88,26 +83,56 @@
   WiFiEndpoint *MakeEndpoint(const string &ssid, const string &bssid,
                              uint16 frequency, int16 signal_dbm) {
     return WiFiEndpoint::MakeOpenEndpoint(
-        NULL, NULL, ssid, bssid, frequency, signal_dbm);
+        NULL, wifi(), ssid, bssid, wpa_supplicant::kNetworkModeInfrastructure,
+        frequency, signal_dbm);
   }
-  WiFiService *MakeGenericService() {
+  WiFiServiceRefPtr MakeSimpleService(const string &security) {
     return new WiFiService(control_interface(),
                            dispatcher(),
                            metrics(),
                            manager(),
-                           wifi(),
-                           vector<uint8_t>(),
+                           &provider_,
+                           simple_ssid_,
                            flimflam::kModeManaged,
-                           flimflam::kSecurityWep,
+                           security,
                            false);
   }
+  WiFiServiceRefPtr MakeGenericService() {
+    return MakeSimpleService(flimflam::kSecurityWep);
+  }
+  void SetWiFiForService(WiFiServiceRefPtr service, WiFiRefPtr wifi) {
+    service->wifi_ = wifi;
+  }
+  WiFiServiceRefPtr MakeServiceWithWiFi(const string &security) {
+    WiFiServiceRefPtr service = MakeSimpleService(security);
+    SetWiFiForService(service, wifi_);
+    return service;
+  }
   ServiceMockAdaptor *GetAdaptor(WiFiService *service) {
     return dynamic_cast<ServiceMockAdaptor *>(service->adaptor());
   }
+  Error::Type TestConfigurePassphrase(const string &security,
+                                      const char *passphrase) {
+    WiFiServiceRefPtr service = MakeSimpleService(security);
+    KeyValueStore args;
+    if (passphrase) {
+      args.SetString(flimflam::kPassphraseProperty, passphrase);
+    }
+    Error error;
+    service->Configure(args, &error);
+    return error.type();
+  }
   scoped_refptr<MockWiFi> wifi() { return wifi_; }
+  MockWiFiProvider *provider() { return &provider_; }
+  string GetAnyDeviceAddress() { return WiFiService::kAnyDeviceAddress; }
+  const vector<uint8_t> &simple_ssid() { return simple_ssid_; }
+  const string &simple_ssid_string() { return simple_ssid_string_; }
 
  private:
   scoped_refptr<MockWiFi> wifi_;
+  MockWiFiProvider provider_;
+  const vector<uint8_t> simple_ssid_;
+  const string simple_ssid_string_;
 };
 
 // static
@@ -128,25 +153,10 @@
 
 class WiFiServiceSecurityTest : public WiFiServiceTest {
  public:
-  WiFiServiceRefPtr CreateServiceWithSecurity(const string &security) {
-    vector<uint8_t> ssid(5);
-    ssid.push_back(0xff);
-
-    return new WiFiService(control_interface(),
-                           dispatcher(),
-                           metrics(),
-                           manager(),
-                           wifi(),
-                           ssid,
-                           flimflam::kModeManaged,
-                           security,
-                           false);
-  }
-
   bool TestStorageSecurityIs(WiFiServiceRefPtr wifi_service,
                              const string &security) {
     string id = wifi_service->GetStorageIdentifier();
-    size_t mac_pos = id.find(StringToLowerASCII(string(fake_mac)));
+    size_t mac_pos = id.find(StringToLowerASCII(GetAnyDeviceAddress()));
     EXPECT_NE(mac_pos, string::npos);
     size_t mode_pos = id.find(string(flimflam::kModeManaged), mac_pos);
     EXPECT_NE(mode_pos, string::npos);
@@ -159,7 +169,7 @@
   // property in to |to_security| as well.
   bool TestStorageMapping(const string &from_security,
                           const string &to_security) {
-    WiFiServiceRefPtr wifi_service = CreateServiceWithSecurity(from_security);
+    WiFiServiceRefPtr wifi_service = MakeSimpleService(from_security);
     NiceMock<MockStore> mock_store;
     EXPECT_CALL(mock_store, SetString(_, _, _)).WillRepeatedly(Return(true));
     EXPECT_CALL(mock_store,
@@ -180,8 +190,7 @@
   bool TestLoadMapping(const string &service_security,
                        const string &storage_security,
                        bool expectation) {
-    WiFiServiceRefPtr wifi_service =
-        CreateServiceWithSecurity(service_security);
+    WiFiServiceRefPtr wifi_service = MakeSimpleService(service_security);
     NiceMock<MockStore> mock_store;
     EXPECT_CALL(mock_store, GetGroupsWithProperties(_))
         .WillRepeatedly(Return(set<string>()));
@@ -220,11 +229,14 @@
         service(MakeGenericService()),
         adaptor(*GetAdaptor(service)) {
     ok_endpoint = MakeEndpoint(
-        "", kOkEndpointBssId, kOkEndpointFrequency, kOkEndpointSignal);
+        simple_ssid_string(), kOkEndpointBssId, kOkEndpointFrequency,
+        kOkEndpointSignal);
     good_endpoint = MakeEndpoint(
-        "", kGoodEndpointBssId, kGoodEndpointFrequency, kGoodEndpointSignal);
+        simple_ssid_string(), kGoodEndpointBssId, kGoodEndpointFrequency,
+        kGoodEndpointSignal);
     bad_endpoint = MakeEndpoint(
-        "", kBadEndpointBssId, kBadEndpointFrequency, kBadEndpointSignal);
+        simple_ssid_string(), kBadEndpointBssId, kBadEndpointFrequency,
+        kBadEndpointSignal);
   }
 
  protected:
@@ -306,25 +318,14 @@
 };
 
 TEST_F(WiFiServiceTest, StorageId) {
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
-
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityNone,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityNone);
   string id = wifi_service->GetStorageIdentifier();
   for (uint i = 0; i < id.length(); ++i) {
     EXPECT_TRUE(id[i] == '_' ||
                 isxdigit(id[i]) ||
                 (isalpha(id[i]) && islower(id[i])));
   }
-  size_t mac_pos = id.find(StringToLowerASCII(string(fake_mac)));
+  size_t mac_pos = id.find(StringToLowerASCII(GetAnyDeviceAddress()));
   EXPECT_NE(mac_pos, string::npos);
   EXPECT_NE(id.find(string(flimflam::kModeManaged), mac_pos), string::npos);
 }
@@ -332,16 +333,7 @@
 // Make sure the passphrase is registered as a write only property
 // by reading and comparing all string properties returned on the store.
 TEST_F(WiFiServiceTest, PassphraseWriteOnly) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWpa,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityWpa);
   ReadablePropertyConstIterator<string> it =
       (wifi_service->store()).GetStringPropertiesIter();
   for( ; !it.AtEnd(); it.Advance())
@@ -354,16 +346,7 @@
   // We only spot check two password cases here to make sure the
   // SetProperty code path does validation.  We're not going to exhaustively
   // test for all types of passwords.
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWep,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityWep);
   Error error;
   EXPECT_TRUE(wifi_service->mutable_store()->SetStringProperty(
                   flimflam::kPassphraseProperty, "0:abcde", &error));
@@ -373,16 +356,7 @@
 }
 
 TEST_F(WiFiServiceTest, PassphraseSetPropertyOpenNetwork) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityNone,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityNone);
   Error error;
   EXPECT_FALSE(wifi_service->mutable_store()->SetStringProperty(
                    flimflam::kPassphraseProperty, "invalid", &error));
@@ -397,7 +371,7 @@
                                                    dispatcher(),
                                                    metrics(),
                                                    manager(),
-                                                   wifi(),
+                                                   provider(),
                                                    ssid,
                                                    flimflam::kModeManaged,
                                                    flimflam::kSecurityNone,
@@ -429,16 +403,7 @@
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskWPA) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWpa,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityWpa);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
   Error error;
@@ -447,16 +412,7 @@
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskRSN) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityRsn,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityRsn);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
   Error error;
@@ -466,16 +422,7 @@
 
 TEST_F(WiFiServiceTest, ConnectConditions) {
   Error error;
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityNone,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityNone);
   scoped_refptr<MockProfile> mock_profile(
       new NiceMock<MockProfile>(control_interface(), manager()));
   wifi_service->set_profile(mock_profile);
@@ -502,16 +449,7 @@
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskPSK) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityPsk,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityPsk);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
   Error error;
@@ -520,23 +458,13 @@
 }
 
 TEST_F(WiFiServiceTest, ConnectTask8021x) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurity8021x,
-                                                   false);
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurity8021x);
   Service::EapCredentials eap;
   eap.identity = "identity";
   eap.password = "mumble";
-  wifi_service->set_eap(eap);
-  EXPECT_CALL(*wifi(),
-              ConnectTo(wifi_service.get(), EAPSecurityArgs()));
-  wifi_service->Connect(NULL);
+  service->set_eap(eap);
+  EXPECT_CALL(*wifi(), ConnectTo(service.get(), EAPSecurityArgs()));
+  service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskAdHocFrequency) {
@@ -546,15 +474,7 @@
   WiFiEndpointRefPtr endpoint_freq =
       MakeEndpoint("a", "00:00:00:00:00:02", 2412, 0);
 
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityNone,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityNone);
   wifi_service->AddEndpoint(endpoint_freq);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), FrequencyArg(false)));
@@ -564,25 +484,27 @@
                                  dispatcher(),
                                  metrics(),
                                  manager(),
-                                 wifi(),
+                                 provider(),
                                  ssid,
                                  flimflam::kModeAdhoc,
                                  flimflam::kSecurityNone,
                                  false);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), FrequencyArg(false)));
+  SetWiFiForService(wifi_service, wifi());
   wifi_service->Connect(NULL);
 
   wifi_service = new WiFiService(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager(),
-                                 wifi(),
+                                 provider(),
                                  ssid,
                                  flimflam::kModeAdhoc,
                                  flimflam::kSecurityNone,
                                  false);
   wifi_service->AddEndpoint(endpoint_nofreq);
+  SetWiFiForService(wifi_service, wifi());
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), FrequencyArg(false)));
   wifi_service->Connect(NULL);
@@ -591,28 +513,20 @@
                                  dispatcher(),
                                  metrics(),
                                  manager(),
-                                 wifi(),
+                                 provider(),
                                  ssid,
                                  flimflam::kModeAdhoc,
                                  flimflam::kSecurityNone,
                                  false);
   wifi_service->AddEndpoint(endpoint_freq);
+  SetWiFiForService(wifi_service, wifi());
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), FrequencyArg(true)));
   wifi_service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskWPA80211w) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityPsk,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityPsk);
   WiFiEndpointRefPtr endpoint = MakeEndpoint("a", "00:00:00:00:00:01", 0, 0);
   endpoint->ieee80211w_required_ = true;
   wifi_service->AddEndpoint(endpoint);
@@ -656,16 +570,7 @@
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskWEP) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWep,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityWep);
   Error error;
   wifi_service->SetPassphrase("0:abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
@@ -702,16 +607,7 @@
 
 // Dynamic WEP + 802.1x.
 TEST_F(WiFiServiceTest, ConnectTaskDynamicWEP) {
-  vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWep,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityWep);
 
   Service::EapCredentials eap;
   eap.key_management = "IEEE8021X";
@@ -725,15 +621,7 @@
 
 TEST_F(WiFiServiceTest, SetPassphraseRemovesCachedCredentials) {
   vector<uint8_t> ssid(5);
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityRsn,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeServiceWithWiFi(flimflam::kSecurityRsn);
 
   const string kPassphrase = "abcdefgh";
 
@@ -792,18 +680,7 @@
 }
 
 TEST_F(WiFiServiceTest, LoadHidden) {
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
-
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   ASSERT_FALSE(service->hidden_ssid_);
   NiceMock<MockStore> mock_store;
   const string storage_id = service->GetStorageIdentifier();
@@ -813,7 +690,7 @@
       .WillRepeatedly(Return(true));
   EXPECT_CALL(mock_store, GetGroupsWithProperties(
       ContainsWiFiProperties(
-          ssid, flimflam::kModeManaged, flimflam::kSecurityNone)))
+          simple_ssid(), flimflam::kModeManaged, flimflam::kSecurityNone)))
       .WillRepeatedly(Return(groups));
   EXPECT_CALL(mock_store, GetBool(_, _, _))
       .WillRepeatedly(Return(false));
@@ -825,16 +702,7 @@
 }
 
 TEST_F(WiFiServiceTest, LoadMultipleMatchingGroups) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurityNone);
   set<string> groups;
   groups.insert("id0");
   groups.insert("id1");
@@ -845,7 +713,7 @@
   NiceMock<MockStore> mock_store;
   EXPECT_CALL(mock_store, GetGroupsWithProperties(
       ContainsWiFiProperties(
-          ssid, flimflam::kModeManaged, flimflam::kSecurityNone)))
+          simple_ssid(), flimflam::kModeManaged, flimflam::kSecurityNone)))
       .WillRepeatedly(Return(groups));
   EXPECT_CALL(mock_store, ContainsGroup(first_group))
       .WillRepeatedly(Return(true));
@@ -903,18 +771,7 @@
 }
 
 TEST_F(WiFiServiceTest, LoadAndUnloadPassphrase) {
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
-
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityPsk,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityPsk);
   NiceMock<MockStore> mock_store;
   const string storage_id = service->GetStorageIdentifier();
   EXPECT_CALL(mock_store, ContainsGroup(StrEq(storage_id)))
@@ -923,7 +780,7 @@
   groups.insert(storage_id);
   EXPECT_CALL(mock_store, GetGroupsWithProperties(
       ContainsWiFiProperties(
-          ssid, flimflam::kModeManaged, flimflam::kSecurityPsk)))
+          simple_ssid(), flimflam::kModeManaged, flimflam::kSecurityPsk)))
       .WillRepeatedly(Return(groups));
   EXPECT_CALL(mock_store, GetBool(_, _, _))
       .WillRepeatedly(Return(false));
@@ -955,18 +812,8 @@
   args.SetString(flimflam::kEAPEAPProperty, "PEAP");
   args.SetString(flimflam::kGuidProperty, guid);
   Error error;
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
 
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurity8021x,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurity8021x);
   // Hack the GUID in so that we don't have to mess about with WiFi to regsiter
   // our service.  This way, Manager will handle the lookup itself.
   service->set_guid(guid);
@@ -977,58 +824,144 @@
   EXPECT_TRUE(service->connectable());
 }
 
+TEST_F(WiFiServiceTest, ConfigurePassphrase) {
+  EXPECT_EQ(Error::kNotSupported,
+            TestConfigurePassphrase(flimflam::kSecurityNone, ""));
+  EXPECT_EQ(Error::kNotSupported,
+            TestConfigurePassphrase(flimflam::kSecurityNone, "foo"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep, NULL));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, ""));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "abcd"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "abcde"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "abcdefghijklm"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "0:abcdefghijklm"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "0102030405"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "0x0102030405"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "O102030405"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "1:O102030405"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "1:0xO102030405"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWep, "0xO102030405"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep,
+                                    "0102030405060708090a0b0c0d"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep,
+                                    "0102030405060708090A0B0C0D"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep,
+                                    "0:0102030405060708090a0b0c0d"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWep,
+                                    "0:0x0102030405060708090a0b0c0d"));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWpa, NULL));
+  EXPECT_EQ(Error::kSuccess,
+            TestConfigurePassphrase(flimflam::kSecurityWpa, "secure password"));
+  EXPECT_EQ(Error::kInvalidPassphrase,
+            TestConfigurePassphrase(flimflam::kSecurityWpa, ""));
+  EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAAsciiMinLen, 'Z').c_str()));
+  EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAAsciiMaxLen, 'Z').c_str()));
+  // subtle: invalid length for hex key, but valid as ascii passphrase
+  EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAHexLen-1, '1').c_str()));
+  EXPECT_EQ(Error::kSuccess, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAHexLen, '1').c_str()));
+  EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAAsciiMinLen-1, 'Z').c_str()));
+  EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAAsciiMaxLen+1, 'Z').c_str()));
+  EXPECT_EQ(Error::kInvalidPassphrase, TestConfigurePassphrase(
+      flimflam::kSecurityWpa,
+      string(IEEE_80211::kWPAHexLen+1, '1').c_str()));
+}
+
+TEST_F(WiFiServiceTest, ConfigureRedundantProperties) {
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
+  KeyValueStore args;
+  args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
+  args.SetString(flimflam::kSSIDProperty, simple_ssid_string());
+  args.SetString(flimflam::kSecurityProperty, flimflam::kSecurityNone);
+  const string kGUID = "aguid";
+  args.SetString(flimflam::kGuidProperty, kGUID);
+
+  EXPECT_EQ("", service->guid());
+  Error error;
+  service->Configure(args, &error);
+  EXPECT_TRUE(error.IsSuccess());
+  EXPECT_EQ(kGUID, service->guid());
+}
+
+TEST_F(WiFiServiceTest, DisconnectWithWiFi) {
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurityWep);
+  EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
+  Error error;
+  service->Disconnect(&error);
+}
+
+TEST_F(WiFiServiceTest, DisconnectWithoutWiFi) {
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityWep);
+  EXPECT_CALL(*wifi(), DisconnectFrom(_)).Times(0);
+  Error error;
+  service->Disconnect(&error);
+  EXPECT_EQ(Error::kOperationFailed, error.type());
+}
+
+TEST_F(WiFiServiceTest, DisconnectWithoutWiFiWhileAssociating) {
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityWep);
+  EXPECT_CALL(*wifi(), DisconnectFrom(_)).Times(0);
+  service->SetState(Service::kStateAssociating);
+  ScopedMockLog log;
+  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
+  EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+                       HasSubstr("WiFi endpoints do not (yet) exist.")));
+  Error error;
+  service->Disconnect(&error);
+  EXPECT_EQ(Error::kOperationFailed, error.type());
+}
+
 TEST_F(WiFiServiceTest, UnloadAndClearCacheWEP) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityWep,
-                                              false);
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurityWep);
   EXPECT_CALL(*wifi(), ClearCachedCredentials(service.get())).Times(1);
   EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
   service->Unload();
 }
 
 TEST_F(WiFiServiceTest, UnloadAndClearCache8021x) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurity8021x,
-                                              false);
+  WiFiServiceRefPtr service = MakeServiceWithWiFi(flimflam::kSecurity8021x);
   EXPECT_CALL(*wifi(), ClearCachedCredentials(service.get())).Times(1);
   EXPECT_CALL(*wifi(), DisconnectFrom(service.get())).Times(1);
   service->Unload();
 }
 
 TEST_F(WiFiServiceTest, ParseStorageIdentifierNone) {
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
-
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   const string storage_id = service->GetStorageIdentifier();
   string address;
   string mode;
   string security;
   EXPECT_TRUE(service->ParseStorageIdentifier(storage_id, &address, &mode,
                                               &security));
-  EXPECT_EQ(StringToLowerASCII(string(fake_mac)), address);
+  EXPECT_EQ(StringToLowerASCII(GetAnyDeviceAddress()), address);
   EXPECT_EQ(flimflam::kModeManaged, mode);
   EXPECT_EQ(flimflam::kSecurityNone, security);
 }
@@ -1036,25 +969,14 @@
 TEST_F(WiFiServiceTest, ParseStorageIdentifier8021x) {
   // Do a separate test for 802.1x, since kSecurity8021x contains a "_",
   // which needs to be dealt with specially in the parser.
-  vector<uint8_t> ssid(5);
-  ssid.push_back(0xff);
-
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurity8021x,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurity8021x);
   const string storage_id = service->GetStorageIdentifier();
   string address;
   string mode;
   string security;
   EXPECT_TRUE(service->ParseStorageIdentifier(storage_id, &address, &mode,
                                               &security));
-  EXPECT_EQ(StringToLowerASCII(string(fake_mac)), address);
+  EXPECT_EQ(StringToLowerASCII(GetAnyDeviceAddress()), address);
   EXPECT_EQ(flimflam::kModeManaged, mode);
   EXPECT_EQ(flimflam::kSecurity8021x, security);
 }
@@ -1154,16 +1076,7 @@
 
 TEST_F(WiFiServiceTest, IsAutoConnectable) {
   const char *reason;
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   EXPECT_CALL(*wifi(), IsIdle())
       .WillRepeatedly(Return(true));
   EXPECT_FALSE(service->HasEndpoints());
@@ -1191,16 +1104,7 @@
 
 TEST_F(WiFiServiceTest, AutoConnect) {
   const char *reason;
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   EXPECT_FALSE(service->IsAutoConnectable(&reason));
   EXPECT_CALL(*wifi(), ConnectTo(_, _))
       .Times(0);
@@ -1223,16 +1127,7 @@
 }
 
 TEST_F(WiFiServiceTest, Populate8021x) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   Service::EapCredentials eap;
   eap.identity = "testidentity";
   eap.pin = "xxxx";
@@ -1254,16 +1149,7 @@
 }
 
 TEST_F(WiFiServiceTest, Populate8021xNoSystemCAs) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   Service::EapCredentials eap;
   eap.identity = "testidentity";
   eap.use_system_cas = false;
@@ -1275,16 +1161,7 @@
 }
 
 TEST_F(WiFiServiceTest, Populate8021xUsingHardwareAuth) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   Service::EapCredentials eap;
   eap.identity = "testidentity";
   eap.key_id = "key_id";
@@ -1301,15 +1178,7 @@
 
 TEST_F(WiFiServiceTest, Populate8021xNSS) {
   vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr service = new WiFiService(control_interface(),
-                                              dispatcher(),
-                                              metrics(),
-                                              manager(),
-                                              wifi(),
-                                              ssid,
-                                              flimflam::kModeManaged,
-                                              flimflam::kSecurityNone,
-                                              false);
+  WiFiServiceRefPtr service = MakeSimpleService(flimflam::kSecurityNone);
   Service::EapCredentials eap;
   eap.ca_cert_nss = "nss_nickname";
   service->set_eap(eap);
@@ -1332,16 +1201,7 @@
 }
 
 TEST_F(WiFiServiceTest, ClearWriteOnlyDerivedProperty) {
-  vector<uint8_t> ssid(1, 'a');
-  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
-                                                   dispatcher(),
-                                                   metrics(),
-                                                   manager(),
-                                                   wifi(),
-                                                   ssid,
-                                                   flimflam::kModeManaged,
-                                                   flimflam::kSecurityWep,
-                                                   false);
+  WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityWep);
 
   EXPECT_EQ("", wifi_service->passphrase_);
 
@@ -1525,7 +1385,7 @@
   EXPECT_CALL(adaptor,
               EmitUint8Changed(flimflam::kSignalStrengthProperty, _)).Times(0);
   ok_endpoint->signal_strength_ = (kOkEndpointSignal + kGoodEndpointSignal) / 2;
-  service->NotifyEndpointUpdated(*ok_endpoint);
+  service->NotifyEndpointUpdated(ok_endpoint);
   Mock::VerifyAndClearExpectations(&adaptor);
 
   // Updating optimal Endpoint updates appropriate Service property.
@@ -1533,7 +1393,7 @@
   EXPECT_CALL(adaptor, EmitStringChanged(flimflam::kWifiBSsid, _)).Times(0);
   EXPECT_CALL(adaptor, EmitUint8Changed(flimflam::kSignalStrengthProperty, _));
   good_endpoint->signal_strength_ = kGoodEndpointSignal + 1;
-  service->NotifyEndpointUpdated(*good_endpoint);
+  service->NotifyEndpointUpdated(good_endpoint);
   Mock::VerifyAndClearExpectations(&adaptor);
 
   // Change in optimal Endpoint updates Service properties.
@@ -1543,7 +1403,7 @@
       flimflam::kWifiBSsid, kOkEndpointBssId));
   EXPECT_CALL(adaptor, EmitUint8Changed(flimflam::kSignalStrengthProperty, _));
   ok_endpoint->signal_strength_ = kGoodEndpointSignal + 2;
-  service->NotifyEndpointUpdated(*ok_endpoint);
+  service->NotifyEndpointUpdated(ok_endpoint);
   Mock::VerifyAndClearExpectations(&adaptor);
 }
 
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index f5dc061..94baab9 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -10,7 +10,6 @@
 #include <linux/netlink.h>  // Needs typedefs from sys/socket.h.
 
 #include <map>
-#include <set>
 #include <string>
 #include <vector>
 
@@ -51,6 +50,7 @@
 #include "shill/mock_supplicant_network_proxy.h"
 #include "shill/mock_supplicant_process_proxy.h"
 #include "shill/mock_time.h"
+#include "shill/mock_wifi_provider.h"
 #include "shill/mock_wifi_service.h"
 #include "shill/nice_mock_control.h"
 #include "shill/property_store_unittest.h"
@@ -62,11 +62,11 @@
 
 
 using std::map;
-using std::set;
 using std::string;
 using std::vector;
 using ::testing::_;
 using ::testing::AnyNumber;
+using ::testing::AtLeast;
 using ::testing::DefaultValue;
 using ::testing::DoAll;
 using ::testing::EndsWith;
@@ -90,7 +90,7 @@
  public:
   WiFiPropertyTest()
       : device_(new WiFi(control_interface(),
-                         NULL, NULL, NULL, "wifi", "", 0)) {
+                         NULL, NULL, manager(), "wifi", "", 0)) {
   }
   virtual ~WiFiPropertyTest() {}
 
@@ -181,6 +181,14 @@
   EXPECT_TRUE(device_->bgscan_method_.empty());
 }
 
+
+MATCHER_P(EndpointMatch, endpoint, "") {
+  return
+      arg->ssid() == endpoint->ssid() &&
+      arg->network_mode() == endpoint->network_mode() &&
+      arg->security_mode() == endpoint->security_mode();
+}
+
 class WiFiObjectTest : public ::testing::TestWithParam<string> {
  public:
   WiFiObjectTest(EventDispatcher *dispatcher)
@@ -195,22 +203,17 @@
                        kDeviceName,
                        kDeviceAddress,
                        0)),
+        bss_counter_(0),
         supplicant_process_proxy_(new NiceMock<MockSupplicantProcessProxy>()),
-        supplicant_interface_proxy_(
-            new NiceMock<MockSupplicantInterfaceProxy>(wifi_)),
         supplicant_bss_proxy_(
             new NiceMock<MockSupplicantBSSProxy>()),
         dhcp_config_(new MockDHCPConfig(&control_interface_,
                                         kDeviceName)),
         dbus_manager_(new NiceMock<MockDBusManager>()),
+        supplicant_interface_proxy_(
+            new NiceMock<MockSupplicantInterfaceProxy>(wifi_)),
         proxy_factory_(this) {
     ::testing::DefaultValue< ::DBus::Path>::Set("/default/path");
-    // Except for WiFiServices created via WiFi::GetService, we expect
-    // that any WiFiService has been registered with the Manager. So
-    // default Manager.HasService to true, to make the common case
-    // easy.
-    ON_CALL(manager_, HasService(_)).
-        WillByDefault(Return(true));
 
     ON_CALL(dhcp_provider_, CreateConfig(_, _, _, _)).
         WillByDefault(Return(dhcp_config_));
@@ -222,6 +225,7 @@
 
     manager_.dbus_manager_.reset(dbus_manager_);  // Transfers ownership.
 
+    wifi_->provider_ = &wifi_provider_;
     wifi_->time_ = &time_;
   }
 
@@ -232,15 +236,14 @@
     wifi_->proxy_factory_ = &proxy_factory_;
     static_cast<Device *>(wifi_)->rtnl_handler_ = &rtnl_handler_;
     wifi_->set_dhcp_provider(&dhcp_provider_);
-    ON_CALL(manager_, device_info()).
-        WillByDefault(Return(&device_info_));
+    ON_CALL(manager_, device_info()).WillByDefault(Return(&device_info_));
     EXPECT_CALL(manager_, UpdateEnabledTechnologies()).Times(AnyNumber());
-    EXPECT_CALL(manager_, DeregisterService(_)).Times(AnyNumber());
     EXPECT_CALL(*supplicant_bss_proxy_, Die()).Times(AnyNumber());
   }
 
   virtual void TearDown() {
-    EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
+    EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(_))
+       .WillRepeatedly(Return(reinterpret_cast<WiFiService *>(NULL)));
     wifi_->SelectService(NULL);
     if (supplicant_bss_proxy_.get()) {
       EXPECT_CALL(*supplicant_bss_proxy_, Die());
@@ -266,10 +269,6 @@
     throw DBus::Error("SomeDBusType", "A handy message");
   }
 
-  void UnloadService(const ServiceRefPtr &service) {
-    service->Unload();
-  }
-
  protected:
   typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;
 
@@ -311,12 +310,180 @@
     WiFiObjectTest *test_;
   };
 
+  // Simulate the course of events when the last enpoint of a service is
+  // removed.
+  class EndpointRemovalHandler {
+    public:
+      EndpointRemovalHandler(WiFiRefPtr wifi, const WiFiServiceRefPtr &service)
+          : wifi_(wifi), service_(service) {}
+      virtual ~EndpointRemovalHandler() {}
+
+      WiFiServiceRefPtr OnEndpointRemoved(
+          const WiFiEndpointConstRefPtr &endpoint) {
+        wifi_->DisassociateFromService(service_);
+        return service_;
+      }
+
+   private:
+    WiFiRefPtr wifi_;
+    WiFiServiceRefPtr service_;
+  };
+
+  EndpointRemovalHandler *MakeEndpointRemovalHandler(
+      WiFiServiceRefPtr &service) {
+    return new EndpointRemovalHandler(wifi_, service);
+  }
   void CancelScanTimer() {
     wifi_->scan_timer_callback_.Cancel();
   }
-  WiFiServiceRefPtr CreateServiceForEndpoint(const WiFiEndpoint &endpoint) {
-    bool hidden_ssid = false;
-    return wifi_->CreateServiceForEndpoint(endpoint, hidden_ssid);
+  // This function creates a new endpoint with a mode set to |mode|.  We
+  // synthesize new |path| and |bssid| values, since we don't really care
+  // what they are for unit tests.  If "use_ssid" is true, we used the
+  // passed-in ssid, otherwise we create a synthesized value for it as well.
+  WiFiEndpointRefPtr MakeNewEndpoint(const char *mode,
+                                     bool use_ssid,
+                                     string *ssid,
+                                     string *path,
+                                     string *bssid) {
+    bss_counter_++;
+    if (!use_ssid) {
+      *ssid = base::StringPrintf("ssid%d", bss_counter_);
+    }
+    *path = base::StringPrintf("/interface/bss%d", bss_counter_);
+    *bssid = base::StringPrintf("00:00:00:00:00:%02x", bss_counter_);
+    WiFiEndpointRefPtr endpoint = MakeEndpointWithMode(*ssid, *bssid, mode);
+    EXPECT_CALL(wifi_provider_,
+                OnEndpointAdded(EndpointMatch(endpoint))).Times(1);
+    return endpoint;
+  }
+  WiFiEndpointRefPtr MakeEndpoint(const string &ssid, const string &bssid) {
+    return MakeEndpointWithMode(ssid, bssid, kNetworkModeInfrastructure);
+  }
+  WiFiEndpointRefPtr MakeEndpointWithMode(
+      const string &ssid, const string &bssid, const string &mode) {
+    return WiFiEndpoint::MakeOpenEndpoint(
+        &proxy_factory_, NULL, ssid, bssid, mode, 0, 0);
+  }
+  MockWiFiServiceRefPtr MakeMockServiceWithSSID(
+      vector<uint8_t> ssid, const std::string &security) {
+    return new NiceMock<MockWiFiService>(
+        &control_interface_,
+        event_dispatcher_,
+        &metrics_,
+        &manager_,
+        &wifi_provider_,
+        ssid,
+        flimflam::kModeManaged,
+        security,
+        false);
+  }
+  MockWiFiServiceRefPtr MakeMockService(const std::string &security) {
+    return MakeMockServiceWithSSID(vector<uint8_t>(1, 'a'), security);
+  }
+  ::DBus::Path MakeNewEndpointAndService(int16_t signal_strength,
+                                         uint16 frequency,
+                                         const char *mode,
+                                         WiFiEndpointRefPtr *endpoint_ptr,
+                                         MockWiFiServiceRefPtr *service_ptr) {
+    string ssid;
+    ::DBus::Path path;
+    string bssid;
+    WiFiEndpointRefPtr endpoint =
+        MakeNewEndpoint(mode, false, &ssid, &path, &bssid);
+    MockWiFiServiceRefPtr service =
+        MakeMockServiceWithSSID(endpoint->ssid(), endpoint->security_mode());
+    EXPECT_CALL(wifi_provider_, FindServiceForEndpoint(EndpointMatch(endpoint)))
+        .WillRepeatedly(Return(service));
+    ON_CALL(*service, GetEndpointCount()).WillByDefault(Return(1));
+    ReportBSS(path, ssid, bssid, signal_strength, frequency, mode);
+    if (service_ptr) {
+      *service_ptr = service;
+    }
+    if (endpoint_ptr) {
+      *endpoint_ptr = endpoint;
+    }
+    return path;
+  }
+  ::DBus::Path AddEndpointToService(
+      WiFiServiceRefPtr service,
+      int16_t signal_strength,
+      uint16 frequency,
+      const char *mode,
+      WiFiEndpointRefPtr *endpoint_ptr) {
+    string ssid(service->ssid().begin(), service->ssid().end());
+    ::DBus::Path path;
+    string bssid;
+    WiFiEndpointRefPtr endpoint =
+        MakeNewEndpoint(mode, true, &ssid, &path, &bssid);
+    EXPECT_CALL(wifi_provider_, FindServiceForEndpoint(EndpointMatch(endpoint)))
+        .WillRepeatedly(Return(service));
+    ReportBSS(path, ssid, bssid, signal_strength, frequency, mode);
+    if (endpoint_ptr) {
+      *endpoint_ptr = endpoint;
+    }
+    return path;
+  }
+  void InitiateConnect(WiFiServiceRefPtr service) {
+    map<string, ::DBus::Variant> params;
+    wifi_->ConnectTo(service, params);
+  }
+  void InitiateDisconnect(WiFiServiceRefPtr service) {
+    wifi_->DisconnectFrom(service);
+  }
+  MockWiFiServiceRefPtr SetupConnectingService(
+      const DBus::Path &network_path,
+      WiFiEndpointRefPtr *endpoint_ptr,
+      ::DBus::Path *bss_path_ptr) {
+    if (!network_path.empty()) {
+      EXPECT_CALL(*GetSupplicantInterfaceProxy(), AddNetwork(_))
+          .WillOnce(Return(network_path));
+      EXPECT_CALL(*GetSupplicantInterfaceProxy(), SelectNetwork(network_path));
+    }
+
+    MockWiFiServiceRefPtr service;
+    WiFiEndpointRefPtr endpoint;
+    ::DBus::Path bss_path(MakeNewEndpointAndService(
+        0, 0, kNetworkModeAdHoc, &endpoint, &service));
+    EXPECT_CALL(*service, SetState(Service::kStateAssociating));
+    InitiateConnect(service);
+    Mock::VerifyAndClearExpectations(service);
+    EXPECT_FALSE(GetPendingTimeout().IsCancelled());
+    if (endpoint_ptr) {
+      *endpoint_ptr = endpoint;
+    }
+    if (bss_path_ptr) {
+      *bss_path_ptr = bss_path;
+    }
+    return service;
+  }
+
+  MockWiFiServiceRefPtr SetupConnectedService(
+      const DBus::Path &network_path,
+      WiFiEndpointRefPtr *endpoint_ptr,
+      ::DBus::Path *bss_path_ptr) {
+    WiFiEndpointRefPtr endpoint;
+    ::DBus::Path bss_path;
+    MockWiFiServiceRefPtr service =
+        SetupConnectingService(network_path, &endpoint, &bss_path);
+    if (endpoint_ptr) {
+      *endpoint_ptr = endpoint;
+    }
+    if (bss_path_ptr) {
+      *bss_path_ptr = bss_path;
+    }
+    EXPECT_CALL(*service, NotifyCurrentEndpoint(EndpointMatch(endpoint)));
+    ReportCurrentBSSChanged(bss_path);
+    EXPECT_TRUE(GetPendingTimeout().IsCancelled());
+    Mock::VerifyAndClearExpectations(service);
+
+    EXPECT_CALL(*service, SetState(Service::kStateConfiguring));
+    EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
+    EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
+    ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
+    Mock::VerifyAndClearExpectations(service);
+
+    EXPECT_EQ(service, GetCurrentService());
+    return service;
   }
   void FireScanTimer() {
     wifi_->ScanTimerHandler();
@@ -348,49 +515,30 @@
   const base::CancelableClosure &GetScanTimer() {
     return wifi_->scan_timer_callback_;
   }
-  const vector<WiFiServiceRefPtr> &GetServices() {
-    return wifi_->services_;
-  }
   // note: the tests need the proxies referenced by WiFi (not the
   // proxies instantiated by WiFiObjectTest), to ensure that WiFi
   // sets up its proxies correctly.
   SupplicantProcessProxyInterface *GetSupplicantProcessProxy() {
     return wifi_->supplicant_process_proxy_.get();
   }
-  MockSupplicantInterfaceProxy *GetSupplicantInterfaceProxy() {
+  MockSupplicantInterfaceProxy *GetSupplicantInterfaceProxyFromWiFi() {
     return dynamic_cast<MockSupplicantInterfaceProxy *>(
         wifi_->supplicant_interface_proxy_.get());
   }
+  // This function returns the supplicant interface proxy whether
+  // or not we have passed the instantiated object to the WiFi instance
+  // from WiFiObjectTest, so tests don't need to worry about when they
+  // set expectations relative to StartWiFi().
+  MockSupplicantInterfaceProxy *GetSupplicantInterfaceProxy() {
+    MockSupplicantInterfaceProxy *proxy = GetSupplicantInterfaceProxyFromWiFi();
+    return proxy ? proxy : supplicant_interface_proxy_.get();
+  }
   MockSupplicantNetworkProxy *CreateSupplicantNetworkProxy() {
     return new NiceMock<MockSupplicantNetworkProxy>();
   }
   const string &GetSupplicantState() {
     return wifi_->supplicant_state_;
   }
-  void InitiateConnect(WiFiServiceRefPtr service) {
-    map<string, ::DBus::Variant> params;
-    wifi_->ConnectTo(service, params);
-  }
-  void InitiateDisconnect(WiFiServiceRefPtr service) {
-    wifi_->DisconnectFrom(service);
-  }
-  WiFiEndpointRefPtr MakeEndpoint(const string &ssid, const string &bssid) {
-    return WiFiEndpoint::MakeOpenEndpoint(
-        &proxy_factory_, NULL, ssid, bssid, 0, 0);
-  }
-  MockWiFiServiceRefPtr MakeMockService(const std::string &security) {
-    vector<uint8_t> ssid(1, 'a');
-    return new MockWiFiService(
-        &control_interface_,
-        event_dispatcher_,
-        &metrics_,
-        &manager_,
-        wifi_,
-        ssid,
-        flimflam::kModeManaged,
-        security,
-        false);
-  }
   void ClearCachedCredentials(const WiFiService *service) {
     return wifi_->ClearCachedCredentials(service);
   }
@@ -455,116 +603,6 @@
   bool GetSupplicantPresent() {
     return wifi_->supplicant_present_;
   }
-  WiFiServiceRefPtr GetOpenService(const char *service_type,
-                                   const char *ssid,
-                                   const char *mode,
-                                   Error *result) {
-    return GetServiceInner(service_type, ssid, mode, NULL, NULL, false, result);
-  }
-  WiFiServiceRefPtr GetService(const char *service_type,
-                               const char *ssid,
-                               const char *mode,
-                               const char *security,
-                               const char *passphrase,
-                               Error *result) {
-    return GetServiceInner(service_type, ssid, mode, security, passphrase,
-                           false, result);
-  }
-  WiFiServiceRefPtr GetServiceInner(const char *service_type,
-                                    const char *ssid,
-                                    const char *mode,
-                                    const char *security,
-                                    const char *passphrase,
-                                    bool allow_hidden,
-                                    Error *result) {
-    map<string, ::DBus::Variant> args;
-    // in general, we want to avoid D-Bus specific code for any RPCs
-    // that come in via adaptors. we make an exception here, because
-    // calls to GetWifiService are rerouted from the Manager object to
-    // the Wifi class.
-    if (service_type != NULL)
-      args[flimflam::kTypeProperty].writer().append_string(service_type);
-    if (ssid != NULL)
-      args[flimflam::kSSIDProperty].writer().append_string(ssid);
-    if (mode != NULL)
-      args[flimflam::kModeProperty].writer().append_string(mode);
-    if (security != NULL)
-      args[flimflam::kSecurityProperty].writer().append_string(security);
-    if (passphrase != NULL)
-      args[flimflam::kPassphraseProperty].writer().append_string(passphrase);
-    if (!allow_hidden)
-      args[flimflam::kWifiHiddenSsid].writer().append_bool(false);
-
-    Error e;
-    KeyValueStore args_kv;
-    DBusAdaptor::ArgsToKeyValueStore(args, &args_kv, &e);
-    WiFiServiceRefPtr service = wifi_->GetService(args_kv, result);
-    if (service) {
-      service->Configure(args_kv, result);
-    }
-    return service;
-  }
-
-  WiFiServiceRefPtr GetServiceWithKeyValues(const KeyValueStore &args,
-                                            Error *result) {
-    return wifi_->GetService(args, result);
-  }
-
-  WiFiServiceRefPtr FindService(const vector<uint8_t> &ssid,
-                                const string &mode,
-                                const string &security) {
-    return wifi_->FindService(ssid, mode, security);
-  }
-  bool LoadHiddenServices(StoreInterface *storage) {
-    return wifi_->LoadHiddenServices(storage);
-  }
-  void SetupHiddenStorage(MockStore *storage, const string &ssid, string *id) {
-    const string hex_ssid = base::HexEncode(ssid.data(), ssid.size());
-    *id = StringToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s",
-                                                flimflam::kTypeWifi,
-                                                kDeviceAddress,
-                                                hex_ssid.c_str(),
-                                                flimflam::kModeManaged,
-                                                flimflam::kSecurityNone));
-    const char *groups[] = { id->c_str() };
-    EXPECT_CALL(*storage, GetGroupsWithKey(flimflam::kWifiHiddenSsid))
-        .WillRepeatedly(Return(set<string>(groups, groups + 1)));
-    EXPECT_CALL(*storage, GetBool(StrEq(*id), flimflam::kWifiHiddenSsid, _))
-        .WillRepeatedly(DoAll(SetArgumentPointee<2>(true), Return(true)));
-    EXPECT_CALL(*storage, GetString(StrEq(*id), flimflam::kSSIDProperty, _))
-        .WillRepeatedly(DoAll(SetArgumentPointee<2>(hex_ssid), Return(true)));
-
-    // For embedded WiFiService::FixupServiceEntries call.
-    EXPECT_CALL(*storage, GetGroups()).WillOnce(Return(set<string>()));
-
-  }
-
-  WiFiService *SetupConnectedService(const DBus::Path &network_path) {
-    EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-    EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-    EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-    EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-    if (!network_path.empty()) {
-      EXPECT_CALL(*supplicant_interface_proxy_, AddNetwork(_))
-          .WillOnce(Return(network_path));
-    }
-
-    StartWiFi();
-    ReportBSS(kBSSName, kSSIDName, "00:00:00:00:00:00", 0, 0,
-              kNetworkModeAdHoc);
-    WiFiService *service(GetServices().begin()->get());
-    EXPECT_TRUE(GetPendingTimeout().IsCancelled());
-    InitiateConnect(service);
-    EXPECT_FALSE(GetPendingTimeout().IsCancelled());
-    ReportCurrentBSSChanged(kBSSName);
-    EXPECT_TRUE(GetPendingTimeout().IsCancelled());
-    ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-
-    EXPECT_EQ(service, GetCurrentService());
-
-    return service;
-  }
-
   bool SetBgscanMethod(const string &method) {
     ::DBus::Error error;
     return DBusAdaptor::SetProperty(
@@ -640,6 +678,10 @@
     return &proxy_factory_;
   }
 
+  MockWiFiProvider *wifi_provider() {
+    return &wifi_provider_;
+  }
+
   EventDispatcher *event_dispatcher_;
   NiceMock<MockRTNLHandler> rtnl_handler_;
   MockTime time_;
@@ -651,6 +693,8 @@
   MockManager manager_;
   MockDeviceInfo device_info_;
   WiFiRefPtr wifi_;
+  NiceMock<MockWiFiProvider> wifi_provider_;
+  int bss_counter_;
 
   // protected fields interspersed between private fields, due to
   // initialization order
@@ -663,13 +707,13 @@
   static const char kSSIDName[];
 
   scoped_ptr<MockSupplicantProcessProxy> supplicant_process_proxy_;
-  scoped_ptr<MockSupplicantInterfaceProxy> supplicant_interface_proxy_;
   scoped_ptr<MockSupplicantBSSProxy> supplicant_bss_proxy_;
   MockDHCPProvider dhcp_provider_;
   scoped_refptr<MockDHCPConfig> dhcp_config_;
   NiceMock<MockDBusManager> *dbus_manager_;
 
  private:
+  scoped_ptr<MockSupplicantInterfaceProxy> supplicant_interface_proxy_;
   NiceMock<TestProxyFactory> proxy_factory_;
 };
 
@@ -732,11 +776,11 @@
 
 TEST_F(WiFiMainTest, ProxiesSetUpDuringStart) {
   EXPECT_TRUE(GetSupplicantProcessProxy() == NULL);
-  EXPECT_TRUE(GetSupplicantInterfaceProxy() == NULL);
+  EXPECT_TRUE(GetSupplicantInterfaceProxyFromWiFi() == NULL);
 
   StartWiFi();
   EXPECT_FALSE(GetSupplicantProcessProxy() == NULL);
-  EXPECT_FALSE(GetSupplicantInterfaceProxy() == NULL);
+  EXPECT_FALSE(GetSupplicantInterfaceProxyFromWiFi() == NULL);
 }
 
 TEST_F(WiFiMainTest, SupplicantPresent) {
@@ -792,18 +836,22 @@
 }
 
 TEST_F(WiFiMainTest, OnSupplicantVanishedWhileConnected) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  WiFiService *service(SetupConnectedService(DBus::Path()));
+  StartWiFi();
+  WiFiEndpointRefPtr endpoint;
+  WiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), &endpoint, NULL));
   ScopedMockLog log;
   EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
   EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
                        EndsWith("silently resetting current_service_.")));
   EXPECT_CALL(*manager(), DeregisterDevice(_))
       .WillOnce(InvokeWithoutArgs(this, &WiFiObjectTest::StopWiFi));
-  EXPECT_CALL(*manager(), DeregisterService(ServiceRefPtr(service)))
-      .WillOnce(Invoke(this, &WiFiObjectTest::UnloadService));
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect()).Times(0);
+  scoped_ptr<EndpointRemovalHandler> handler(
+      MakeEndpointRemovalHandler(service));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint)))
+      .WillOnce(Invoke(handler.get(),
+                &EndpointRemovalHandler::OnEndpointRemoved));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
   EXPECT_CALL(*manager(), RegisterDevice(_));
   OnSupplicantVanish();
   EXPECT_TRUE(GetCurrentService() == NULL);
@@ -817,33 +865,31 @@
           DBus::Error(
               "fi.w1.wpa_supplicant1.InterfaceUnknown",
               "test threw fi.w1.wpa_supplicant1.InterfaceUnknown")));
-  EXPECT_CALL(*manager(), LoadDeviceFromProfiles(_));
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
   EXPECT_TRUE(GetScanTimer().IsCancelled());
   StartWiFi();
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
   dispatcher_.DispatchPendingEvents();
   EXPECT_FALSE(GetScanTimer().IsCancelled());
 }
 
 TEST_F(WiFiMainTest, ClearCachedCredentials) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
+  StartWiFi();
   DBus::Path network = "/test/path";
-  WiFiService *service(SetupConnectedService(network));
-  EXPECT_CALL(supplicant_interface_proxy, RemoveNetwork(network));
+  WiFiServiceRefPtr service(SetupConnectedService(network, NULL, NULL));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(network));
   ClearCachedCredentials(service);
 }
 
 TEST_F(WiFiMainTest, RemoveNetwork) {
   DBus::Path network = "/test/path";
-  EXPECT_CALL(*supplicant_interface_proxy_, RemoveNetwork(network));
   StartWiFi();
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(network));
   EXPECT_TRUE(RemoveNetwork(network));
 }
 
 TEST_F(WiFiMainTest, RemoveNetworkWhenSupplicantReturnsNetworkUnknown) {
   DBus::Path network = "/test/path";
-  EXPECT_CALL(*supplicant_interface_proxy_, RemoveNetwork(network))
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(network))
       .WillRepeatedly(Throw(
           DBus::Error(
               "fi.w1.wpa_supplicant1.NetworkUnknown",
@@ -860,7 +906,7 @@
 
 TEST_F(WiFiMainTest, RemoveNetworkWhenSupplicantReturnsInvalidArgs) {
   DBus::Path network = "/test/path";
-  EXPECT_CALL(*supplicant_interface_proxy_, RemoveNetwork(network))
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(network))
       .WillRepeatedly(Throw(
           DBus::Error(
               "fi.w1.wpa_supplicant1.InvalidArgs",
@@ -871,7 +917,7 @@
 
 TEST_F(WiFiMainTest, RemoveNetworkWhenSupplicantReturnsUnknownError) {
   DBus::Path network = "/test/path";
-  EXPECT_CALL(*supplicant_interface_proxy_, RemoveNetwork(network))
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(network))
       .WillRepeatedly(Throw(
           DBus::Error(
               "fi.w1.wpa_supplicant1.UnknownError",
@@ -888,54 +934,60 @@
               "fi.w1.wpa_supplicant1.InterfaceExists",
               "test threw fi.w1.wpa_supplicant1.InterfaceExists")));
   EXPECT_CALL(*supplicant_process_proxy_, GetInterface(_));
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
   StartWiFi();
   dispatcher_.DispatchPendingEvents();
 }
 
 TEST_F(WiFiMainTest, StartClearsState) {
-  EXPECT_CALL(*supplicant_interface_proxy_, RemoveAllNetworks());
-  EXPECT_CALL(*supplicant_interface_proxy_, FlushBSS(_));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveAllNetworks());
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), FlushBSS(_));
   StartWiFi();
 }
 
 TEST_F(WiFiMainTest, NoScansWhileConnecting) {
-  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
   StartWiFi();
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+  dispatcher_.DispatchPendingEvents();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
   SetPendingService(service);
   // If we're connecting, we ignore scan requests to stay on channel.
-  service->state_ = Service::kStateConfiguring;
+  EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
   TriggerScan();
   dispatcher_.DispatchPendingEvents();
   Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
-  // But otherwise we'll honor the request.
-  service->state_ = Service::kStateConnected;
+  Mock::VerifyAndClearExpectations(service);
+  EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
   TriggerScan();
   dispatcher_.DispatchPendingEvents();
   Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  Mock::VerifyAndClearExpectations(service);
   // Similarly, ignore scans when our connected service is reconnecting.
   SetPendingService(NULL);
   SetCurrentService(service);
-  service->state_ = Service::kStateAssociating;
+  EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
   TriggerScan();
   dispatcher_.DispatchPendingEvents();
   Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  Mock::VerifyAndClearExpectations(service);
   // But otherwise we'll honor the request.
-  service->state_ = Service::kStateIdle;
+  EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
   TriggerScan();
   dispatcher_.DispatchPendingEvents();
   Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  Mock::VerifyAndClearExpectations(service);
 }
 
 TEST_F(WiFiMainTest, ResumeStartsScanWhenIdle) {
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
   StartWiFi();
   dispatcher_.DispatchPendingEvents();
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy_);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
   ReportScanDone();
   ASSERT_TRUE(wifi()->IsIdle());
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
@@ -944,10 +996,10 @@
 }
 
 TEST_F(WiFiMainTest, SuspendDoesNotStartScan) {
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
   StartWiFi();
   dispatcher_.DispatchPendingEvents();
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy_);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
   ASSERT_TRUE(wifi()->IsIdle());
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
   OnBeforeSuspend();
@@ -955,20 +1007,13 @@
 }
 
 TEST_F(WiFiMainTest, ResumeDoesNotStartScanWhenNotIdle) {
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
   StartWiFi();
-
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-  Error error;
-  ScopedMockLog log;
-  service->AddEndpoint(ap);
-  service->AutoConnect();
-  service->SetState(Service::kStateOnline);
-  EXPECT_FALSE(wifi()->IsIdle());
   dispatcher_.DispatchPendingEvents();
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy_);
-  ASSERT_FALSE(wifi()->IsIdle());
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  WiFiServiceRefPtr service(SetupConnectedService(DBus::Path(), NULL, NULL));
+  EXPECT_FALSE(wifi()->IsIdle());
+  ScopedMockLog log;
   EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
   EXPECT_CALL(log, Log(_, _, EndsWith("already scanning or connected.")));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
@@ -977,7 +1022,7 @@
 }
 
 TEST_F(WiFiMainTest, ScanResults) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
+  EXPECT_CALL(*wifi_provider(), OnEndpointAdded(_)).Times(5);
   StartWiFi();
   ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
   ReportBSS(
@@ -1007,294 +1052,127 @@
   EXPECT_EQ("adhoc", i->second->network_mode());
 }
 
-TEST_F(WiFiMainTest, ScanResultsWithUpdates) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS(
-      "bss1", "ssid1", "00:00:00:00:00:01", 1, 0, kNetworkModeInfrastructure);
-  ReportBSS(
-      "bss2", "ssid2", "00:00:00:00:00:02", 2, 0, kNetworkModeInfrastructure);
-  ReportBSS(
-      "bss1", "ssid1", "00:00:00:00:00:01", 3, 0, kNetworkModeInfrastructure);
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 4, 0, kNetworkModeAdHoc);
-
-  const WiFi::EndpointMap &endpoints_by_rpcid = GetEndpointMap();
-  EXPECT_EQ(3, endpoints_by_rpcid.size());
-
-  WiFi::EndpointMap::const_iterator i;
-  WiFiEndpointRefPtr endpoint;
-  for (i = endpoints_by_rpcid.begin();
-       i != endpoints_by_rpcid.end();
-       ++i) {
-    if (i->second->bssid_string() == "00:00:00:00:00:00")
-      break;
-  }
-  ASSERT_TRUE(i != endpoints_by_rpcid.end());
-  EXPECT_EQ(4, i->second->signal_strength());
-}
-
 TEST_F(WiFiMainTest, ScanCompleted) {
   StartWiFi();
-  EXPECT_CALL(*manager(), RegisterService(_))
-      .Times(3);
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS(
-      "bss1", "ssid1", "00:00:00:00:00:01", 1, 0, kNetworkModeInfrastructure);
-  ReportBSS(
-      "bss2", "ssid2", "00:00:00:00:00:02", 2, 0, kNetworkModeInfrastructure);
+  WiFiEndpointRefPtr ap0 = MakeEndpointWithMode("ssid0", "00:00:00:00:00:00",
+                                                kNetworkModeAdHoc);
+  WiFiEndpointRefPtr ap1 = MakeEndpoint("ssid1", "00:00:00:00:00:01");
+  WiFiEndpointRefPtr ap2 = MakeEndpoint("ssid2", "00:00:00:00:00:02");
+  EXPECT_CALL(*wifi_provider(), OnEndpointAdded(EndpointMatch(ap0))).Times(1);
+  EXPECT_CALL(*wifi_provider(), OnEndpointAdded(EndpointMatch(ap1))).Times(1);
+  EXPECT_CALL(*wifi_provider(), OnEndpointAdded(EndpointMatch(ap2))).Times(1);
+  ReportBSS("bss0", ap0->ssid_string(), ap0->bssid_string(), 0, 0,
+            kNetworkModeAdHoc);
+  ReportBSS("bss1", ap1->ssid_string(), ap1->bssid_string(), 0, 0,
+            kNetworkModeInfrastructure);
+  ReportBSS("bss2", ap2->ssid_string(), ap2->bssid_string(), 0, 0,
+            kNetworkModeInfrastructure);
   ReportScanDone();
-  EXPECT_EQ(3, GetServices().size());
+  Mock::VerifyAndClearExpectations(wifi_provider());
+
+  EXPECT_CALL(*wifi_provider(), OnEndpointAdded(_)).Times(0);
 
   // BSSes with SSIDs that start with NULL should be filtered.
   ReportBSS("bss3", string(1, 0), "00:00:00:00:00:03", 3, 0, kNetworkModeAdHoc);
-  EXPECT_EQ(3, GetEndpointMap().size());
-  EXPECT_EQ(3, GetServices().size());
 
   // BSSes with empty SSIDs should be filtered.
   ReportBSS("bss3", string(), "00:00:00:00:00:03", 3, 0, kNetworkModeAdHoc);
-  EXPECT_EQ(3, GetEndpointMap().size());
-  EXPECT_EQ(3, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, EndpointGroupingTogether) {
-  StartWiFi();
-
-  InSequence s;
-  EXPECT_CALL(*manager(), RegisterService(_));
-  EXPECT_CALL(*manager(), HasService(_));
-  EXPECT_CALL(*manager(), UpdateService(_));
-  ReportBSS("bss0", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid", "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  EXPECT_EQ(1, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, EndpointGroupingDifferentSSID) {
-  StartWiFi();
-  EXPECT_CALL(*manager(), RegisterService(_))
-      .Times(2);
-  ReportBSS("bss0", "ssid1", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid2", "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  EXPECT_EQ(2, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, EndpointGroupingDifferentMode) {
-  StartWiFi();
-  EXPECT_CALL(*manager(), RegisterService(_))
-      .Times(2);
-  ReportBSS("bss0", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid", "00:00:00:00:00:01", 0, 0,
-            kNetworkModeInfrastructure);
-  ReportScanDone();
-  EXPECT_EQ(2, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, NonExistentBSSRemoved) {
-  // Removal of non-existent BSS should not cause a crash.
-  StartWiFi();
-  RemoveBSS("bss0");
-  EXPECT_EQ(0, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, BSSWithEmptySSIDRemoved) {
-  // Removal of BSS with an empty SSID should not cause a crash.
-  ReportBSS("bss", string(), "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  StartWiFi();
-  RemoveBSS("bss");
-  EXPECT_EQ(0, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, BSSWithNullSSIDRemoved) {
-  // Removal of BSS with a NULL SSID should not cause a crash.
-  ReportBSS("bss", string(1, 0), "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  StartWiFi();
-  RemoveBSS("bss");
-  EXPECT_EQ(0, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, LoneBSSRemoved) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  StartWiFi();
-  ReportBSS("bss0", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  EXPECT_EQ(1, GetServices().size());
-  EXPECT_TRUE(GetServices().front()->IsVisible());
-
-  EXPECT_CALL(*manager(), DeregisterService(_));
-  RemoveBSS("bss0");
-  EXPECT_TRUE(GetServices().empty());
 }
 
 TEST_F(WiFiMainTest, LoneBSSRemovedWhileConnected) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
   StartWiFi();
-  ReportBSS("bss0", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  ReportCurrentBSSChanged("bss0");
-
+  WiFiEndpointRefPtr endpoint;
+  DBus::Path bss_path;
+  WiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), &endpoint, &bss_path));
+  scoped_ptr<EndpointRemovalHandler> handler(
+      MakeEndpointRemovalHandler(service));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint)))
+      .WillOnce(Invoke(handler.get(),
+                &EndpointRemovalHandler::OnEndpointRemoved));
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
-  EXPECT_CALL(*manager(), DeregisterService(_));
-  RemoveBSS("bss0");
-  EXPECT_TRUE(GetServices().empty());
-}
-
-TEST_F(WiFiMainTest, LoneBSSRemovedWhileConnectedToHidden) {
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  StartWiFi();
-
-  Error e;
-  WiFiServiceRefPtr service =
-      GetServiceInner(flimflam::kTypeWifi, "ssid", flimflam::kModeManaged,
-                      NULL, NULL, true, &e);
-  EXPECT_EQ(1, GetServices().size());
-
-  ReportBSS("bss", "ssid", "00:00:00:00:00:01", 0, 0,
-            kNetworkModeInfrastructure);
-  ReportScanDone();
-  ReportCurrentBSSChanged("bss");
-  EXPECT_EQ(1, GetServices().size());
-
-  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
-  RemoveBSS("bss");
-  EXPECT_EQ(1, GetServices().size());
-  // Verify expectations now, because WiFi may call UpdateService when
-  // WiFi is Stop()-ed (during TearDown()).
-  Mock::VerifyAndClearExpectations(manager());
-  EXPECT_CALL(*manager(), DeregisterService(_)).Times(AnyNumber());
+  RemoveBSS(bss_path);
 }
 
 TEST_F(WiFiMainTest, NonSolitaryBSSRemoved) {
-  EXPECT_CALL(*manager(), RegisterService(_));
-  EXPECT_CALL(*manager(), HasService(_));
-  EXPECT_CALL(*manager(), UpdateService(_));
   StartWiFi();
-  ReportBSS("bss0", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  EXPECT_EQ(1, GetServices().size());
-  EXPECT_TRUE(GetServices().front()->IsVisible());
-
-  EXPECT_CALL(*manager(), UpdateService(_));
-  RemoveBSS("bss0");
-  EXPECT_TRUE(GetServices().front()->IsVisible());
-  EXPECT_EQ(1, GetServices().size());
-}
-
-TEST_F(WiFiMainTest, Connect) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
-  StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-
-  {
-    InSequence s;
-    DBus::Path fake_path("/fake/path");
-    WiFiService *service(GetServices().begin()->get());
-
-    EXPECT_CALL(supplicant_interface_proxy, AddNetwork(_))
-        .WillOnce(Return(fake_path));
-    EXPECT_CALL(supplicant_interface_proxy, SelectNetwork(fake_path));
-    InitiateConnect(service);
-    EXPECT_EQ(static_cast<Service *>(service),
-              wifi()->selected_service_.get());
-    EXPECT_EQ(Service::kStateAssociating, service->state());
-    EXPECT_FALSE(GetPendingTimeout().IsCancelled());
-  }
+  WiFiEndpointRefPtr endpoint;
+  DBus::Path bss_path;
+  WiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), &endpoint, &bss_path));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint)))
+     .WillOnce(Return(reinterpret_cast<WiFiService *>(NULL)));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
+  RemoveBSS(bss_path);
 }
 
 TEST_F(WiFiMainTest, ReconnectPreservesDBusPath) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-
-  DBus::Path fake_path("/fake/path");
-  WiFiService *service(GetServices().begin()->get());
-  EXPECT_CALL(supplicant_interface_proxy, AddNetwork(_))
-      .WillOnce(Return(fake_path));
-  EXPECT_CALL(supplicant_interface_proxy, SelectNetwork(fake_path));
-  InitiateConnect(service);
-
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy);
+  DBus::Path kPath = "/test/path";
+  WiFiServiceRefPtr service(SetupConnectedService(kPath, NULL, NULL));
 
   // Return the service to a connectable state.
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
   InitiateDisconnect(service);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+
+  // Complete the disconnection by reporting a BSS change.
+  ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
 
   // A second connection attempt should remember the DBus path associated
   // with this service.
-  EXPECT_CALL(supplicant_interface_proxy, AddNetwork(_)).Times(0);
-  EXPECT_CALL(supplicant_interface_proxy, SelectNetwork(fake_path));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), AddNetwork(_)).Times(0);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), SelectNetwork(kPath));
   InitiateConnect(service);
 }
 
 TEST_F(WiFiMainTest, DisconnectPendingService) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service(GetServices().begin()->get());
-  InitiateConnect(service);
-
-  EXPECT_FALSE(GetPendingService() == NULL);
-  EXPECT_TRUE(service->IsConnecting());
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect());
+  MockWiFiServiceRefPtr service(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
+  EXPECT_TRUE(GetPendingService() == service.get());
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
+  EXPECT_CALL(*service, SetState(Service::kStateIdle)).Times(AtLeast(1));
   InitiateDisconnect(service);
-  EXPECT_FALSE(service->IsConnecting());
-
+  Mock::VerifyAndClearExpectations(service.get());
   EXPECT_TRUE(GetPendingService() == NULL);
 }
 
 TEST_F(WiFiMainTest, DisconnectPendingServiceWithCurrent) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid1", "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service0(GetServices()[0].get());
-  WiFiService *service1(GetServices()[1].get());
+  MockWiFiServiceRefPtr service0(
+      SetupConnectedService(DBus::Path(), NULL, NULL));
+  EXPECT_EQ(service0, GetCurrentService());
+  EXPECT_EQ(NULL, GetPendingService().get());
 
-  InitiateConnect(service0);
-  ReportCurrentBSSChanged("bss0");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  InitiateConnect(service1);
+  // We don't explicitly call Disconnect() while transitioning to a new
+  // service.  Instead, we use the side-effect of SelectNetwork (verified in
+  // SetupConnectingService).
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
+  MockWiFiServiceRefPtr service1(
+      SetupConnectingService("/new/path", NULL, NULL));
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
 
   EXPECT_EQ(service0, GetCurrentService());
   EXPECT_EQ(service1, GetPendingService());
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect());
+  EXPECT_CALL(*service1, SetState(Service::kStateIdle)).Times(AtLeast(1));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
   InitiateDisconnect(service1);
+  Mock::VerifyAndClearExpectations(service1.get());
 
   // |current_service_| will be unchanged until supplicant signals
   // that CurrentBSS has changed.
   EXPECT_EQ(service0, GetCurrentService());
   // |pending_service_| is updated immediately.
-  EXPECT_TRUE(GetPendingService() == NULL);
+  EXPECT_EQ(NULL, GetPendingService().get());
   EXPECT_TRUE(GetPendingTimeout().IsCancelled());
 }
 
 TEST_F(WiFiMainTest, DisconnectCurrentService) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  DBus::Path fake_path("/fake/path");
-  WiFiService *service(SetupConnectedService(fake_path));
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect());
+  StartWiFi();
+  ::DBus::Path kPath("/fake/path");
+  MockWiFiServiceRefPtr service(SetupConnectedService(kPath, NULL, NULL));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
   InitiateDisconnect(service);
 
   // |current_service_| should not change until supplicant reports
@@ -1304,22 +1182,22 @@
   // Expect that the entry associated with this network will be disabled.
   MockSupplicantNetworkProxy *network_proxy = CreateSupplicantNetworkProxy();
   EXPECT_CALL(*proxy_factory(), CreateSupplicantNetworkProxy(
-      fake_path, wpa_supplicant::kDBusAddr))
+      kPath, wpa_supplicant::kDBusAddr))
       .WillOnce(Return(network_proxy));
   EXPECT_CALL(*network_proxy, SetEnabled(false));
-  EXPECT_CALL(supplicant_interface_proxy, RemoveNetwork(fake_path)).Times(0);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(kPath)).Times(0);
   ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
-  EXPECT_TRUE(GetCurrentService() == NULL);
+  EXPECT_EQ(NULL, GetCurrentService().get());
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
 }
 
 TEST_F(WiFiMainTest, DisconnectCurrentServiceWithErrors) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  DBus::Path fake_path("/fake/path");
-  WiFiService *service(SetupConnectedService(fake_path));
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect())
+  StartWiFi();
+  DBus::Path kPath("/fake/path");
+  WiFiServiceRefPtr service(SetupConnectedService(kPath, NULL, NULL));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect())
       .WillOnce(InvokeWithoutArgs(this, (&WiFiMainTest::ThrowDBusError)));
-  EXPECT_CALL(supplicant_interface_proxy, RemoveNetwork(fake_path)).Times(1);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(kPath)).Times(1);
   InitiateDisconnect(service);
 
   // We may sometimes fail to disconnect via supplicant, and we patch up some
@@ -1329,28 +1207,12 @@
 }
 
 TEST_F(WiFiMainTest, DisconnectCurrentServiceWithPending) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportBSS("bss1", "ssid1", "00:00:00:00:00:01", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service0(GetServices()[0].get());
-  WiFiService *service1(GetServices()[1].get());
-
-  InitiateConnect(service0);
-  ReportCurrentBSSChanged("bss0");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  InitiateConnect(service1);
-
+  WiFiServiceRefPtr service0(SetupConnectedService(DBus::Path(), NULL, NULL));
+  WiFiServiceRefPtr service1(SetupConnectingService(DBus::Path(), NULL, NULL));
   EXPECT_EQ(service0, GetCurrentService());
   EXPECT_EQ(service1, GetPendingService());
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect())
-      .Times(0);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
   InitiateDisconnect(service0);
 
   EXPECT_EQ(service0, GetCurrentService());
@@ -1360,87 +1222,76 @@
 
 TEST_F(WiFiMainTest, TimeoutPendingService) {
   StartWiFi();
-  dispatcher_.DispatchPendingEvents();
   const base::CancelableClosure &pending_timeout = GetPendingTimeout();
   EXPECT_TRUE(pending_timeout.IsCancelled());
-
-  InSequence seq;
-  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
-  EXPECT_CALL(*service, SetState(Service::kStateAssociating));
-  InitiateConnect(service);
+  MockWiFiServiceRefPtr service(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
   EXPECT_FALSE(pending_timeout.IsCancelled());
   EXPECT_EQ(service, GetPendingService());
-
-  {
-    InSequence seq;
-    EXPECT_CALL(*service, SetState(Service::kStateIdle));
-    EXPECT_CALL(*service, SetFailure(Service::kFailureOutOfRange));
-    pending_timeout.callback().Run();
-  }
-  EXPECT_EQ(NULL, GetPendingService().get());
-
-  // Verify expectations now, because WiFi may report other state changes
-  // when WiFi is Stop()-ed (during TearDown()).
-  Mock::VerifyAndClearExpectations(service.get());
+  EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureOutOfRange, _));
+  pending_timeout.callback().Run();
 }
 
 TEST_F(WiFiMainTest, DisconnectInvalidService) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service(GetServices().begin()->get());
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect())
-      .Times(0);
+  MockWiFiServiceRefPtr service;
+  MakeNewEndpointAndService(0, 0, kNetworkModeAdHoc, NULL, &service);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
   InitiateDisconnect(service);
 }
 
 TEST_F(WiFiMainTest, DisconnectCurrentServiceFailure) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  DBus::Path fake_path("/fake/path");
-  WiFiService *service(SetupConnectedService(fake_path));
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect())
+  StartWiFi();
+  DBus::Path kPath("/fake/path");
+  WiFiServiceRefPtr service(SetupConnectedService(kPath, NULL, NULL));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect())
       .WillRepeatedly(Throw(
           DBus::Error(
               "fi.w1.wpa_supplicant1.NotConnected",
               "test threw fi.w1.wpa_supplicant1.NotConnected")));
-  EXPECT_CALL(supplicant_interface_proxy, RemoveNetwork(fake_path));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(kPath));
   InitiateDisconnect(service);
-
-  EXPECT_TRUE(GetCurrentService() == NULL);
+  EXPECT_EQ(NULL, GetCurrentService().get());
 }
 
 TEST_F(WiFiMainTest, Stop) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
+  WiFiEndpointRefPtr endpoint0;
+  ::DBus::Path kPath("/fake/path");
+  WiFiServiceRefPtr service0(SetupConnectedService(kPath, &endpoint0, NULL));
+  WiFiEndpointRefPtr endpoint1;
+  MakeNewEndpointAndService(0, 0, kNetworkModeAdHoc, &endpoint1, NULL);
 
-  EXPECT_CALL(*manager(), DeregisterService(_));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint0)))
+     .WillOnce(Return(reinterpret_cast<WiFiService *>(NULL)));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint1)))
+     .WillOnce(Return(reinterpret_cast<WiFiService *>(NULL)));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveNetwork(kPath)).Times(1);
   StopWiFi();
   EXPECT_TRUE(GetScanTimer().IsCancelled());
   EXPECT_FALSE(wifi()->weak_ptr_factory_.HasWeakPtrs());
 }
 
 TEST_F(WiFiMainTest, StopWhileConnected) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  WiFiService *service(SetupConnectedService(DBus::Path()));
-  EXPECT_CALL(*manager(), DeregisterService(ServiceRefPtr(service)))
-      .WillOnce(Invoke(this, &WiFiObjectTest::UnloadService));
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect());
+  StartWiFi();
+  WiFiEndpointRefPtr endpoint;
+  WiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), &endpoint, NULL));
+  scoped_ptr<EndpointRemovalHandler> handler(
+      MakeEndpointRemovalHandler(service));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(EndpointMatch(endpoint)))
+      .WillOnce(Invoke(handler.get(),
+                &EndpointRemovalHandler::OnEndpointRemoved));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
   StopWiFi();
   EXPECT_TRUE(GetCurrentService() == NULL);
 }
 
 TEST_F(WiFiMainTest, ReconnectTimer) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  WiFiService *service(SetupConnectedService(DBus::Path()));
-  service->SetState(Service::kStateConnected);
+  StartWiFi();
+  MockWiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), NULL, NULL));
+  EXPECT_CALL(*service, IsConnected()).WillRepeatedly(Return(true));
   EXPECT_TRUE(GetReconnectTimeoutCallback().IsCancelled());
   ReportStateChanged(wpa_supplicant::kInterfaceStateDisconnected);
   EXPECT_FALSE(GetReconnectTimeoutCallback().IsCancelled());
@@ -1452,284 +1303,12 @@
   EXPECT_TRUE(GetReconnectTimeoutCallback().IsCancelled());
   ReportStateChanged(wpa_supplicant::kInterfaceStateDisconnected);
   EXPECT_FALSE(GetReconnectTimeoutCallback().IsCancelled());
-  EXPECT_CALL(supplicant_interface_proxy, Disconnect());
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
   GetReconnectTimeoutCallback().callback().Run();
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy_);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
   EXPECT_TRUE(GetReconnectTimeoutCallback().IsCancelled());
 }
 
-TEST_F(WiFiMainTest, GetWifiServiceOpen) {
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged, &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceOpenNoSSID) {
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, NULL, flimflam::kModeManaged, &e);
-  EXPECT_EQ(Error::kInvalidArguments, e.type());
-  EXPECT_EQ("must specify SSID", e.message());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceOpenLongSSID) {
-  Error e;
-  GetOpenService(
-      flimflam::kTypeWifi, "123456789012345678901234567890123",
-      flimflam::kModeManaged, &e);
-  EXPECT_EQ(Error::kInvalidNetworkName, e.type());
-  EXPECT_EQ("SSID is too long", e.message());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceOpenShortSSID) {
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, "", flimflam::kModeManaged, &e);
-  EXPECT_EQ(Error::kInvalidNetworkName, e.type());
-  EXPECT_EQ("SSID is too short", e.message());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceOpenBadMode) {
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, "an_ssid", "ad-hoc", &e);
-  EXPECT_EQ(Error::kNotSupported, e.type());
-  EXPECT_EQ("service mode is unsupported", e.message());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceOpenNoMode) {
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, "an_ssid", NULL, &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceRSN) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityRsn, "secure password", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceRSNNoPassword) {
-  // When configuring hidden networks, Chrome expects to be able to
-  // GetService w/o a password, and supply the password with
-  // SetProperty afterwards.
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityRsn, NULL, &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceBadSecurity) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged, "rot-13",
-             NULL, &e);
-  EXPECT_EQ(Error::kNotSupported, e.type());
-  EXPECT_EQ("security mode is unsupported", e.message());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEPNoPassword) {
-  // When configuring hidden networks, Chrome expects to be able to
-  // GetService w/o a password, and supply the password with
-  // SetProperty afterwards.
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, NULL, &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEPEmptyPassword) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "", &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40ASCII) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "abcde", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP104ASCII) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "abcdefghijklm", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40ASCIIWithKeyIndex) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0:abcdefghijklm", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40Hex) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0102030405", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40HexBadPassphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "O102030405", &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40HexWithKeyIndexBadPassphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "1:O102030405", &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40HexWithKeyIndexAndBaseBadPassphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "1:0xO102030405", &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP40HexWithBaseBadPassphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0xO102030405", &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP104Hex) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0102030405060708090a0b0c0d", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP104HexUppercase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0102030405060708090A0B0C0D", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP104HexWithKeyIndex) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0:0102030405060708090a0b0c0d", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_F(WiFiMainTest, GetWifiServiceWEP104HexWithKeyIndexAndBase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWep, "0:0x0102030405060708090a0b0c0d", &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-class WiFiGetServiceSuccessTest : public WiFiMainTest {};
-class WiFiGetServiceFailureTest : public WiFiMainTest {};
-
-TEST_P(WiFiGetServiceSuccessTest, Passphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWpa, GetParam().c_str(), &e);
-  EXPECT_TRUE(e.IsSuccess());
-}
-
-TEST_P(WiFiGetServiceFailureTest, Passphrase) {
-  Error e;
-  GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-             flimflam::kSecurityWpa, GetParam().c_str(), &e);
-  EXPECT_EQ(Error::kInvalidPassphrase, e.type());
-}
-
-INSTANTIATE_TEST_CASE_P(
-    WiFiGetServiceSuccessTestInstance,
-    WiFiGetServiceSuccessTest,
-    Values(
-        string(IEEE_80211::kWPAAsciiMinLen, 'Z'),
-        string(IEEE_80211::kWPAAsciiMaxLen, 'Z'),
-        // subtle: invalid length for hex key, but valid as ascii passphrase
-        string(IEEE_80211::kWPAHexLen-1, '1'),
-        string(IEEE_80211::kWPAHexLen, '1')));
-
-INSTANTIATE_TEST_CASE_P(
-    WiFiGetServiceFailureTestInstance,
-    WiFiGetServiceFailureTest,
-    Values(
-        string(IEEE_80211::kWPAAsciiMinLen-1, 'Z'),
-        string(IEEE_80211::kWPAAsciiMaxLen+1, 'Z'),
-        string(IEEE_80211::kWPAHexLen+1, '1')));
-
-TEST_F(WiFiMainTest, FindServiceWEP) {
-  const string ssid("an_ssid");
-  {
-    Error e;
-    GetService(flimflam::kTypeWifi, ssid.c_str(), flimflam::kModeManaged,
-               flimflam::kSecurityWep, "abcde", &e);
-    EXPECT_TRUE(e.IsSuccess());
-  }
-  vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
-
-  EXPECT_TRUE(FindService(ssid_bytes, flimflam::kModeManaged,
-                          flimflam::kSecurityWep).get());
-  EXPECT_FALSE(FindService(ssid_bytes, flimflam::kModeManaged,
-                           flimflam::kSecurityWpa).get());
-}
-
-TEST_F(WiFiMainTest, FindServiceWPA) {
-  const string ssid("an_ssid");
-  {
-    Error e;
-    GetService(flimflam::kTypeWifi, ssid.c_str(), flimflam::kModeManaged,
-               flimflam::kSecurityRsn, "abcdefgh", &e);
-    EXPECT_TRUE(e.IsSuccess());
-  }
-  vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
-  WiFiServiceRefPtr wpa_service(FindService(ssid_bytes, flimflam::kModeManaged,
-                                            flimflam::kSecurityWpa));
-  EXPECT_TRUE(wpa_service.get());
-  WiFiServiceRefPtr rsn_service(FindService(ssid_bytes, flimflam::kModeManaged,
-                                            flimflam::kSecurityRsn));
-  EXPECT_TRUE(rsn_service.get());
-  EXPECT_EQ(wpa_service.get(), rsn_service.get());
-  WiFiServiceRefPtr psk_service(FindService(ssid_bytes, flimflam::kModeManaged,
-                                            flimflam::kSecurityPsk));
-  EXPECT_EQ(wpa_service.get(), psk_service.get());
-  // Indirectly test FindService by doing a GetService on something that
-  // already exists.
-  {
-    Error e;
-    WiFiServiceRefPtr wpa_service2(
-        GetServiceInner(flimflam::kTypeWifi, ssid.c_str(),
-                        flimflam::kModeManaged, flimflam::kSecurityWpa,
-                        "abcdefgh", false, &e));
-    EXPECT_TRUE(e.IsSuccess());
-    EXPECT_EQ(wpa_service.get(), wpa_service2.get());
-  }
-}
-
-TEST_F(WiFiMainTest, GetServiceWithGUID) {
-  // Perform a GetService and also configure properties in the base Service
-  // class using Service::Configure().
-  KeyValueStore args;
-  args.SetString(flimflam::kTypeProperty, flimflam::kTypeWifi);
-  args.SetString(flimflam::kSSIDProperty, "ssid");
-  args.SetString(flimflam::kSecurityProperty, flimflam::kSecurityNone);
-  const string kGUID = "aguid";  // Stored as a registered Service property.
-  args.SetString(flimflam::kGuidProperty, kGUID);
-
-  Error e;
-  WiFiServiceRefPtr service = GetServiceWithKeyValues(args, &e);
-  EXPECT_TRUE(e.IsSuccess());
-  // Assert that before Configure is called on the service, the GUID property
-  // is not set.
-  EXPECT_EQ("", service->guid());
-
-  service->Configure(args, &e);
-  EXPECT_TRUE(e.IsSuccess());
-  EXPECT_EQ(kGUID, service->guid());
-}
 
 MATCHER_P(HasHiddenSSID, ssid, "") {
   map<string, DBus::Variant>::const_iterator it =
@@ -1745,52 +1324,31 @@
   // two SSID entries: one containing the SSID we are looking for,
   // and an empty entry, signifying that we also want to do a
   // broadcast probe request for all non-hidden APs as well.
-  return ssids.size() == 2 &&
-      string(ssids[0].begin(), ssids[0].end()) == ssid &&
-      ssids[1].empty();
+  return ssids.size() == 2 && ssids[0] == ssid && ssids[1].empty();
+}
+
+MATCHER(HasNoHiddenSSID, "") {
+  map<string, DBus::Variant>::const_iterator it =
+      arg.find(wpa_supplicant::kPropertyScanSSIDs);
+  return it == arg.end();
 }
 
 TEST_F(WiFiMainTest, ScanHidden) {
-  EXPECT_CALL(*supplicant_process_proxy_, CreateInterface(_));
-  EXPECT_CALL(*supplicant_process_proxy_, GetInterface(_))
-      .Times(AnyNumber())
-      .WillRepeatedly(Throw(
-          DBus::Error(
-              "fi.w1.wpa_supplicant1.InterfaceUnknown",
-              "test threw fi.w1.wpa_supplicant1.InterfaceUnknown")));
-  scoped_refptr<MockProfile> profile(
-      new NiceMock<MockProfile>(control_interface(), manager(), ""));
-  {
-    // Create a hidden service with an associated profile.
-    Error e;
-    WiFiServiceRefPtr service =
-        GetServiceInner(flimflam::kTypeWifi, "ssid0", flimflam::kModeManaged,
-                        NULL, NULL, true, &e);
-    EXPECT_TRUE(e.IsSuccess());
-    EXPECT_TRUE(service->hidden_ssid());
-    service->set_profile(profile);
-  }
-  {
-    // Create a hidden service without an associated profile.
-    Error e;
-    WiFiServiceRefPtr service =
-        GetServiceInner(flimflam::kTypeWifi, "ssid1", flimflam::kModeManaged,
-                        NULL, NULL, true, &e);
-    EXPECT_TRUE(e.IsSuccess());
-    EXPECT_TRUE(service->hidden_ssid());
-  }
-  {
-    // Create a non-hidden service with an associated profile.
-    Error e;
-    WiFiServiceRefPtr service =
-        GetServiceInner(flimflam::kTypeWifi, "ssid2", flimflam::kModeManaged,
-                        NULL, NULL, false, &e);
-    EXPECT_TRUE(e.IsSuccess());
-    EXPECT_FALSE(service->hidden_ssid());
-    service->set_profile(profile);
-  }
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(HasHiddenSSID("ssid0")));
+  vector<uint8_t>kSSID(1, 'a');
+  ByteArrays ssids;
+  ssids.push_back(kSSID);
+
   StartWiFi();
+  EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList()).WillOnce(Return(ssids));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasHiddenSSID(kSSID)));
+  dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, ScanNoHidden) {
+  StartWiFi();
+  EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
+      .WillOnce(Return(ByteArrays()));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasNoHiddenSSID()));
   dispatcher_.DispatchPendingEvents();
 }
 
@@ -1799,7 +1357,7 @@
   EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
   EXPECT_CALL(log, Log(_, _, EndsWith(
       "Ignoring scan request while device is not enabled."))).Times(1);
-  EXPECT_CALL(*supplicant_interface_proxy_, Scan(_)).Times(0);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
   StartWiFi();
   StopWiFi();
   // A scan is queued when WiFi resumes.
@@ -1848,8 +1406,8 @@
             GetSupplicantState());
   // Verify expectations now, because WiFi may report other state changes
   // when WiFi is Stop()-ed (during TearDown()).
-  Mock::VerifyAndClearExpectations(service.get());
-  EXPECT_CALL(*service.get(), SetState(_)).Times(AnyNumber());
+  Mock::VerifyAndClearExpectations(service);
+  EXPECT_CALL(*service, SetState(_)).Times(AnyNumber());
 }
 
 TEST_F(WiFiMainTest, ConnectToServiceWithoutRecentIssues) {
@@ -1894,257 +1452,69 @@
   ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
 }
 
-TEST_F(WiFiMainTest, LoadHiddenServicesFailWithNoGroups) {
-  StrictMock<MockStore> storage;
-  EXPECT_CALL(storage, GetGroups()).WillOnce(Return(set<string>()));
-  EXPECT_CALL(*metrics(), SendEnumToUMA(_, _, _)).Times(0);
-  EXPECT_CALL(storage, GetGroupsWithKey(flimflam::kWifiHiddenSsid))
-      .WillOnce(Return(set<string>()));
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-TEST_F(WiFiMainTest, LoadHiddenServicesWithFixedUpServices) {
-  StrictMock<MockStore> storage;
-  EXPECT_CALL(*manager(), IsDefaultProfile(&storage))
-      .WillOnce(Return(true));
-  EXPECT_CALL(*metrics(), SendEnumToUMA(
-      "Network.Shill.Wifi.ServiceFixupEntries",
-      Metrics::kMetricServiceFixupDefaultProfile,
-      Metrics::kMetricServiceFixupMax)).Times(1);
-  EXPECT_CALL(storage, Flush()).Times(1);
-  const string kGroupId =
-      StringPrintf("%s_%s_0_%s_%s",
-                   flimflam::kTypeWifi,
-                   kDeviceAddress,
-                   flimflam::kModeManaged,
-                   flimflam::kSecurityNone);
-  EXPECT_CALL(storage, GetString(kGroupId, _, _)).WillRepeatedly(Return(false));
-  EXPECT_CALL(storage, SetString(kGroupId, _, _)).WillRepeatedly(Return(true));
-  set<string> groups;
-  groups.insert(kGroupId);
-  EXPECT_CALL(storage, GetGroups()).WillRepeatedly(Return(groups));
-  EXPECT_CALL(storage, GetGroupsWithKey(flimflam::kWifiHiddenSsid))
-      .WillRepeatedly(Return(set<string>()));
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-  Mock::VerifyAndClearExpectations(metrics());
-
-  EXPECT_CALL(*manager(), IsDefaultProfile(&storage))
-      .WillOnce(Return(false));
-  EXPECT_CALL(*metrics(), SendEnumToUMA(
-      "Network.Shill.Wifi.ServiceFixupEntries",
-      Metrics::kMetricServiceFixupUserProfile,
-      Metrics::kMetricServiceFixupMax)).Times(1);
-  EXPECT_CALL(storage, Flush()).Times(1);
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-TEST_F(WiFiMainTest, LoadHiddenServicesFailWithMissingHidden) {
-  string id;
-  StrictMock<MockStore> storage;
-  SetupHiddenStorage(&storage, "an_ssid", &id);
-  // Missing "Hidden" property.
-  EXPECT_CALL(storage, GetBool(StrEq(id), flimflam::kWifiHiddenSsid, _))
-      .WillOnce(Return(false));
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-TEST_F(WiFiMainTest, LoadHiddenServicesFailWithFalseHidden) {
-  string id;
-  StrictMock<MockStore> storage;
-  SetupHiddenStorage(&storage, "an_ssid", &id);
-  // "Hidden" property set to "false".
-  EXPECT_CALL(storage, GetBool(StrEq(id), flimflam::kWifiHiddenSsid, _))
-      .WillOnce(DoAll(SetArgumentPointee<2>(true), Return(false)));
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-TEST_F(WiFiMainTest, LoadHiddenServicesFailWithMissingSSID) {
-  string id;
-  StrictMock<MockStore> storage;
-  SetupHiddenStorage(&storage, "an_ssid", &id);
-  // Missing "SSID" property.
-  EXPECT_CALL(storage, GetString(StrEq(id), flimflam::kSSIDProperty, _))
-      .WillOnce(Return(false));
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-
-TEST_F(WiFiMainTest, LoadHiddenServicesFailWithFoundService) {
-  StrictMock<MockStore> storage;
-  string id;
-  SetupHiddenStorage(&storage, "an_ssid", &id);
-  Error e;
-  GetOpenService(flimflam::kTypeWifi, "an_ssid", NULL, &e);
-  ASSERT_TRUE(e.IsSuccess());
-  EXPECT_FALSE(LoadHiddenServices(&storage));
-}
-
-TEST_F(WiFiMainTest, LoadHiddenServicesSuccess) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  StrictMock<MockStore> storage;
-  string ssid("an_ssid");
-  string id;
-  SetupHiddenStorage(&storage, ssid, &id);
-  EXPECT_TRUE(LoadHiddenServices(&storage));
-  vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
-  EXPECT_TRUE(FindService(ssid_bytes, flimflam::kModeManaged,
-                          flimflam::kSecurityNone).get());
-}
-
 TEST_F(WiFiMainTest, CurrentBSSChangeConnectedToDisconnected) {
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-
-  // Note that the BSS handle used in this test ("an_ap") is not
-  // intended to reflect the format used by supplicant. It's just
-  // convenient for testing.
-
   StartWiFi();
-  ReportBSS("an_ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-  EXPECT_EQ(service, GetPendingService().get());
+  WiFiEndpointRefPtr endpoint;
+  MockWiFiServiceRefPtr service =
+      SetupConnectedService(DBus::Path(), &endpoint, NULL);
 
-  ReportCurrentBSSChanged("an_ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  EXPECT_EQ(Service::kStateConfiguring, service->state());
-  EXPECT_EQ(service, GetCurrentService().get());
-  EXPECT_EQ(NULL, GetPendingService().get());
-
+  EXPECT_CALL(*service, SetState(Service::kStateIdle));
+  EXPECT_CALL(*service, SetFailureSilent(Service::kFailureUnknown));
   ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
-  EXPECT_EQ(Service::kStateIdle, service->state());
-  EXPECT_TRUE(service->IsFailed());
   EXPECT_EQ(NULL, GetCurrentService().get());
   EXPECT_EQ(NULL, GetPendingService().get());
 }
 
 TEST_F(WiFiMainTest, CurrentBSSChangeConnectedToConnectedNewService) {
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  WiFiEndpointRefPtr ap1 = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiEndpointRefPtr ap2 = MakeEndpoint("another_ssid", "01:02:03:04:05:06");
-  WiFiServiceRefPtr service1 = CreateServiceForEndpoint(*ap1);
-  WiFiServiceRefPtr service2 = CreateServiceForEndpoint(*ap2);
-
-  // Note that the BSS handles used in this test ("ap1", "ap2") are
-  // not intended to reflect the format used by supplicant. They're
-  // just convenient for testing.
-
   StartWiFi();
-  ReportBSS("ap1", ap1->ssid_string(), ap1->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  ReportBSS("ap2", ap2->ssid_string(), ap2->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service1);
-  ReportCurrentBSSChanged("ap1");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  EXPECT_EQ(service1.get(), GetCurrentService().get());
+  MockWiFiServiceRefPtr service0 =
+      SetupConnectedService(DBus::Path(), NULL, NULL);
+  MockWiFiServiceRefPtr service1;
+  ::DBus::Path bss_path1(MakeNewEndpointAndService(
+      0, 0, kNetworkModeAdHoc, NULL, &service1));
+  EXPECT_EQ(service0.get(), GetCurrentService().get());
 
   // Note that we deliberately omit intermediate supplicant states
   // (e.g. kInterfaceStateAssociating), on the theory that they are
   // unreliable. Specifically, they may be quashed if the association
   // completes before supplicant flushes its changed properties.
-  ReportCurrentBSSChanged("ap2");
+  EXPECT_CALL(*service0, SetState(Service::kStateIdle)).Times(AtLeast(1));
+  ReportCurrentBSSChanged(bss_path1);
+  EXPECT_CALL(*service1, SetState(Service::kStateConfiguring));
   ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  EXPECT_EQ(service2.get(), GetCurrentService().get());
-  EXPECT_EQ(Service::kStateIdle, service1->state());
-  EXPECT_EQ(Service::kStateConfiguring, service2->state());
-}
-
-TEST_F(WiFiMainTest, CurrentBSSChangeDisconnectedToConnected) {
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(AnyNumber());
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-
-  // Note that the BSS handle used in this test ("an_ap") is not
-  // intended to reflect the format used by supplicant. It's just
-  // convenient for testing.
-
-  StartWiFi();
-  ReportBSS("an_ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-  ReportCurrentBSSChanged("an_ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  EXPECT_EQ(service.get(), GetCurrentService().get());
-  EXPECT_EQ(Service::kStateConfiguring, service->state());
+  EXPECT_EQ(service1.get(), GetCurrentService().get());
+  Mock::VerifyAndClearExpectations(service0);
+  Mock::VerifyAndClearExpectations(service1);
 }
 
 TEST_F(WiFiMainTest, CurrentBSSChangedUpdateServiceEndpoint) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-
-  const uint16 frequency1 = 2412;
-  const uint16 frequency2 = 2442;
   StartWiFi();
-  ReportBSS("bss1", "ssid1", "00:00:00:00:00:01", 1, frequency1,
-            kNetworkModeInfrastructure);
-  ReportBSS("bss2", "ssid1", "00:00:00:00:00:02", 1, frequency2,
-            kNetworkModeInfrastructure);
-  EXPECT_EQ(1, GetServices().size());
-  WiFiService *service(GetServices()[0].get());
-  InitiateConnect(service);
-  ReportCurrentBSSChanged("bss1");
-  EXPECT_EQ(frequency1, service->frequency_);
-  EXPECT_EQ("00:00:00:00:00:01", service->bssid_);
-  ReportCurrentBSSChanged("bss2");
-  EXPECT_EQ(frequency2, service->frequency_);
-  EXPECT_EQ("00:00:00:00:00:02", service->bssid_);
-}
-
-TEST_F(WiFiMainTest, ConfiguredServiceRegistration) {
-  Error e;
-  EXPECT_CALL(*manager(), RegisterService(_))
-      .Times(0);
-  EXPECT_CALL(*manager(), HasService(_))
-      .WillOnce(Return(false));
-  GetOpenService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged, &e);
-  EXPECT_CALL(*manager(), RegisterService(_));
-  ReportBSS("ap0", "an_ssid", "00:00:00:00:00:00", 0, 0,
-            kNetworkModeInfrastructure);
+  MockWiFiServiceRefPtr service =
+      SetupConnectedService(DBus::Path(), NULL, NULL);
+  WiFiEndpointRefPtr endpoint;
+  ::DBus::Path bss_path =
+      AddEndpointToService(service, 0, 0, kNetworkModeAdHoc, &endpoint);
+  EXPECT_CALL(*service, NotifyCurrentEndpoint(EndpointMatch(endpoint)));
+  ReportCurrentBSSChanged(bss_path);
 }
 
 TEST_F(WiFiMainTest, NewConnectPreemptsPending) {
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  WiFiEndpointRefPtr ap1 = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiEndpointRefPtr ap2 = MakeEndpoint("another_ssid", "01:02:03:04:05:06");
-  WiFiServiceRefPtr service1 = CreateServiceForEndpoint(*ap1);
-  WiFiServiceRefPtr service2 = CreateServiceForEndpoint(*ap2);
-
   StartWiFi();
-  ReportBSS("ap1", ap1->ssid_string(), ap1->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  ReportBSS("ap2", ap2->ssid_string(), ap2->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service1);
-  EXPECT_EQ(service1.get(), GetPendingService().get());
-
+  MockWiFiServiceRefPtr service0(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
+  EXPECT_EQ(service0.get(), GetPendingService().get());
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
-  EXPECT_CALL(*GetSupplicantInterfaceProxy(), AddNetwork(_));
-  InitiateConnect(service2);
-  EXPECT_EQ(service2.get(), GetPendingService().get());
+  MockWiFiServiceRefPtr service1(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
+  EXPECT_EQ(service1.get(), GetPendingService().get());
+  EXPECT_EQ(NULL, GetCurrentService().get());
 }
 
 TEST_F(WiFiMainTest, IsIdle) {
   StartWiFi();
   EXPECT_TRUE(wifi()->IsIdle());
-
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-  Error error;
-  service->AddEndpoint(ap);
-  service->AutoConnect();
+  MockWiFiServiceRefPtr service(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
   EXPECT_FALSE(wifi()->IsIdle());
 }
 
@@ -2154,49 +1524,39 @@
 }
 
 TEST_F(WiFiMainTest, AddNetworkArgs) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service(GetServices().begin()->get());
-  EXPECT_CALL(supplicant_interface_proxy, AddNetwork(WiFiAddedArgs(true)));
+  MockWiFiServiceRefPtr service;
+  MakeNewEndpointAndService(0, 0, kNetworkModeAdHoc, NULL, &service);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), AddNetwork(WiFiAddedArgs(true)));
   EXPECT_TRUE(SetBgscanMethod(wpa_supplicant::kNetworkBgscanMethodSimple));
   InitiateConnect(service);
 }
 
 TEST_F(WiFiMainTest, AddNetworkArgsNoBgscan) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  WiFiService *service(GetServices().begin()->get());
-  EXPECT_CALL(supplicant_interface_proxy, AddNetwork(WiFiAddedArgs(false)));
+  MockWiFiServiceRefPtr service;
+  MakeNewEndpointAndService(0, 0, kNetworkModeAdHoc, NULL, &service);
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), AddNetwork(WiFiAddedArgs(false)));
   InitiateConnect(service);
 }
 
 TEST_F(WiFiMainTest, AppendBgscan) {
   StartWiFi();
-  WiFiEndpointRefPtr ap1 = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap1);
-  service->AddEndpoint(ap1);
-  EXPECT_EQ(1, service->GetEndpointCount());
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
   {
     // 1 endpoint, default bgscan method -- background scan disabled.
     std::map<std::string, DBus::Variant> params;
-    AppendBgscan(service.get(), &params);
+    EXPECT_CALL(*service, GetEndpointCount()).WillOnce(Return(1));
+    AppendBgscan(service, &params);
+    Mock::VerifyAndClearExpectations(service);
     EXPECT_FALSE(ContainsKey(params, wpa_supplicant::kNetworkPropertyBgscan));
   }
-  WiFiEndpointRefPtr ap2 = MakeEndpoint("an_ssid", "01:02:03:04:05:06");
-  service->AddEndpoint(ap2);
-  EXPECT_EQ(2, service->GetEndpointCount());
   {
     // 2 endpoints, default bgscan method -- background scan frequency reduced.
     map<string, DBus::Variant> params;
-    AppendBgscan(service.get(), &params);
+    EXPECT_CALL(*service, GetEndpointCount()).WillOnce(Return(2));
+    AppendBgscan(service, &params);
+    Mock::VerifyAndClearExpectations(service);
     string config_string;
     EXPECT_TRUE(
         DBusProperties::GetString(params,
@@ -2213,7 +1573,9 @@
     // Explicit bgscan method -- regular background scan frequency.
     EXPECT_TRUE(SetBgscanMethod(wpa_supplicant::kNetworkBgscanMethodSimple));
     std::map<std::string, DBus::Variant> params;
-    AppendBgscan(service.get(), &params);
+    EXPECT_CALL(*service, GetEndpointCount()).Times(0);
+    AppendBgscan(service, &params);
+    Mock::VerifyAndClearExpectations(service);
     string config_string;
     EXPECT_TRUE(
         DBusProperties::GetString(params,
@@ -2229,7 +1591,9 @@
     // No scan method, simply returns without appending properties
     EXPECT_TRUE(SetBgscanMethod(wpa_supplicant::kNetworkBgscanMethodNone));
     std::map<std::string, DBus::Variant> params;
+    EXPECT_CALL(*service, GetEndpointCount()).Times(0);
     AppendBgscan(service.get(), &params);
+    Mock::VerifyAndClearExpectations(service);
     string config_string;
     EXPECT_FALSE(
         DBusProperties::GetString(params,
@@ -2240,8 +1604,8 @@
 
 TEST_F(WiFiMainTest, StateAndIPIgnoreLinkEvent) {
   StartWiFi();
-  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
-  InitiateConnect(service);
+  MockWiFiServiceRefPtr service(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
   EXPECT_CALL(*service.get(), SetState(_)).Times(0);
   EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(0);
   ReportLinkUp();
@@ -2249,69 +1613,32 @@
   // Verify expectations now, because WiFi may cause |service| state
   // changes during TearDown().
   Mock::VerifyAndClearExpectations(service);
-  EXPECT_CALL(*service.get(), SetState(_)).Times(AnyNumber());
-}
-
-TEST_F(WiFiMainTest, SupplicantCompleted) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(supplicant_interface_proxy, EnableHighBitrates()).Times(0);
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-
-  StartWiFi();
-  ReportBSS("ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP());
-  ReportCurrentBSSChanged("ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
-  EXPECT_EQ(Service::kStateConfiguring, service->state());
 }
 
 TEST_F(WiFiMainTest, SupplicantCompletedAlreadyConnected) {
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
-  EXPECT_CALL(*dhcp_config_.get(), RequestIP());
-  EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
-  EXPECT_CALL(*device_info(), FlushAddresses(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), device_info()).Times(AnyNumber());
-  EXPECT_CALL(*dhcp_provider(), CreateConfig(_, _, _, _)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), HasService(_)).Times(AnyNumber());
-  EXPECT_CALL(*manager(), IsPortalDetectionEnabled(_)).Times(AnyNumber());
-  EXPECT_CALL(supplicant_interface_proxy, EnableHighBitrates()).Times(0);
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-
   StartWiFi();
-  ReportBSS("ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-  ReportCurrentBSSChanged("ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
+  MockWiFiServiceRefPtr service(
+      SetupConnectedService(DBus::Path(), NULL, NULL));
   Mock::VerifyAndClearExpectations(dhcp_config_.get());
   EXPECT_CALL(*dhcp_config_.get(), RequestIP()).Times(0);
   // Simulate a rekeying event from the AP.  These show as transitions from
   // completed->completed from wpa_supplicant.
   ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
   // When we get an IP, WiFi should enable high bitrates on the interface again.
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy);
-  EXPECT_CALL(supplicant_interface_proxy, EnableHighBitrates()).Times(1);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), EnableHighBitrates()).Times(1);
+  EXPECT_CALL(*manager(), device_info()).WillOnce(Return(device_info()));
   ReportIPConfigComplete();
   // Similarly, rekeying events after we have an IP don't trigger L3
   // configuration.  However, we treat all transitions to completed as potential
   // reassociations, so we will reenable high rates again here.
-  Mock::VerifyAndClearExpectations(&supplicant_interface_proxy);
-  EXPECT_CALL(supplicant_interface_proxy, EnableHighBitrates()).Times(1);
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  EXPECT_CALL(*service, IsConnected()).WillOnce(Return(true));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), EnableHighBitrates()).Times(1);
   ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
 }
 
 TEST_F(WiFiMainTest, BSSAddedCreatesBSSProxy) {
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
   // TODO(quiche): Consider using a factory for WiFiEndpoints, so that
   // we can test the interaction between WiFi and WiFiEndpoint. (Right
   // now, we're testing across multiple layers.)
@@ -2324,14 +1651,15 @@
 TEST_F(WiFiMainTest, BSSRemovedDestroysBSSProxy) {
   // TODO(quiche): As for BSSAddedCreatesBSSProxy, consider using a
   // factory for WiFiEndpoints.
-  EXPECT_CALL(*manager(), RegisterService(_)).Times(AnyNumber());
-
   // Get the pointer before we transfer ownership.
   MockSupplicantBSSProxy *proxy = supplicant_bss_proxy_.get();
   EXPECT_CALL(*proxy, Die());
   StartWiFi();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  RemoveBSS("bss0");
+  DBus::Path bss_path(
+      MakeNewEndpointAndService(0, 0, kNetworkModeAdHoc, NULL, NULL));
+  EXPECT_CALL(*wifi_provider(), OnEndpointRemoved(_))
+     .WillOnce(Return(reinterpret_cast<WiFiService *>(NULL)));
+  RemoveBSS(bss_path);
   // Check this now, to make sure RemoveBSS killed the proxy (rather
   // than TearDown).
   Mock::VerifyAndClearExpectations(proxy);
@@ -2340,15 +1668,13 @@
 TEST_F(WiFiMainTest, FlushBSSOnResume) {
   const struct timeval resume_time = {1, 0};
   const struct timeval scan_done_time = {6, 0};
-  MockSupplicantInterfaceProxy &supplicant_interface_proxy =
-      *supplicant_interface_proxy_;
 
   StartWiFi();
 
   EXPECT_CALL(time_, GetTimeMonotonic(_))
       .WillOnce(DoAll(SetArgumentPointee<0>(resume_time), Return(0)))
       .WillOnce(DoAll(SetArgumentPointee<0>(scan_done_time), Return(0)));
-  EXPECT_CALL(supplicant_interface_proxy,
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(),
               FlushBSS(WiFi::kMaxBSSResumeAgeSeconds + 5));
   OnAfterResume();
   ReportScanDone();
@@ -2384,10 +1710,8 @@
 TEST_F(WiFiMainTest, ScanTimerConnecting) {
   StartWiFi();
   dispatcher_.DispatchPendingEvents();
-  ReportBSS("bss0", "ssid0", "00:00:00:00:00:00", 0, 0, kNetworkModeAdHoc);
-  ReportScanDone();
-  WiFiService *service(GetServices().begin()->get());
-  InitiateConnect(service);
+  MockWiFiServiceRefPtr service =
+      SetupConnectingService(DBus::Path(), NULL, NULL);
   CancelScanTimer();
   EXPECT_TRUE(GetScanTimer().IsCancelled());
 
@@ -2424,42 +1748,26 @@
 }
 
 TEST_F(WiFiMainTest, ScanOnDisconnectWithHidden) {
-  Error e;
-  scoped_refptr<MockProfile> profile(
-      new NiceMock<MockProfile>(control_interface(), manager(), ""));
-  WiFiServiceRefPtr hidden_service =
-      GetServiceInner(flimflam::kTypeWifi, "hidden_ssid",
-                      flimflam::kModeManaged, NULL, NULL, true, &e);
-  hidden_service->set_profile(profile);
-
   StartWiFi();
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-  ReportBSS("an_ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-  ReportCurrentBSSChanged("an_ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
   dispatcher_.DispatchPendingEvents();
-
-  EXPECT_CALL(*GetSupplicantInterfaceProxy(),
-              Scan(HasHiddenSSID("hidden_ssid")));
+  SetupConnectedService(DBus::Path(), NULL, NULL);
+  vector<uint8_t>kSSID(1, 'a');
+  ByteArrays ssids;
+  ssids.push_back(kSSID);
+  EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
+      .WillRepeatedly(Return(ssids));
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasHiddenSSID(kSSID)));
   ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
   dispatcher_.DispatchPendingEvents();
 }
 
 TEST_F(WiFiMainTest, NoScanOnDisconnectWithoutHidden) {
   StartWiFi();
-  WiFiEndpointRefPtr ap = MakeEndpoint("an_ssid", "00:01:02:03:04:05");
-  WiFiServiceRefPtr service = CreateServiceForEndpoint(*ap);
-  ReportBSS("an_ap", ap->ssid_string(), ap->bssid_string(), 0, 0,
-            kNetworkModeInfrastructure);
-  InitiateConnect(service);
-  ReportCurrentBSSChanged("an_ap");
-  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
   dispatcher_.DispatchPendingEvents();
-
+  SetupConnectedService(DBus::Path(), NULL, NULL);
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+  EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
+      .WillRepeatedly(Return(ByteArrays()));
   ReportCurrentBSSChanged(wpa_supplicant::kCurrentBSSNull);
   dispatcher_.DispatchPendingEvents();
 }
@@ -2482,25 +1790,22 @@
   EXPECT_CALL(*GetSupplicantInterfaceProxy(), Reassociate()).Times(1);
   OnLinkMonitorFailure();
   OnSupplicantVanish();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Reassociate()).Times(0);
   EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
                        EndsWith("Cannot reassociate."))).Times(1);
   OnLinkMonitorFailure();
 }
 
 TEST_F(WiFiMainTest, SuspectCredentialsOpen) {
-  Error e;
-  WiFiServiceRefPtr service = GetOpenService(
-      flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged, &e);
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
   ReportStateChanged(wpa_supplicant::kInterfaceState4WayHandshake);
   EXPECT_FALSE(service->has_ever_connected());
   EXPECT_FALSE(SuspectCredentials(*service, NULL));
 }
 
 TEST_F(WiFiMainTest, SuspectCredentialsWPANeverConnected) {
-  Error e;
-  WiFiServiceRefPtr service =
-      GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-                 flimflam::kSecurityWpa, "abcdefgh", &e);
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityWpa);
   ReportStateChanged(wpa_supplicant::kInterfaceState4WayHandshake);
   EXPECT_FALSE(service->has_ever_connected());
   Service::ConnectFailure failure;
@@ -2509,20 +1814,14 @@
 }
 
 TEST_F(WiFiMainTest, SuspectCredentialsWPAPreviouslyConnected) {
-  Error e;
-  WiFiServiceRefPtr service =
-      GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-                 flimflam::kSecurityWpa, "abcdefgh", &e);
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityWpa);
   ReportStateChanged(wpa_supplicant::kInterfaceState4WayHandshake);
   service->has_ever_connected_ = true;
   EXPECT_FALSE(SuspectCredentials(*service, NULL));
 }
 
 TEST_F(WiFiMainTest, SuspectCredentialsEAPInProgress) {
-  Error e;
-  WiFiServiceRefPtr service =
-      GetService(flimflam::kTypeWifi, "an_ssid", flimflam::kModeManaged,
-                 flimflam::kSecurity8021x, "abcdefgh", &e);
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurity8021x);
   SetCurrentService(service);
   service->has_ever_connected_ = false;
   EXPECT_FALSE(SuspectCredentials(*service, NULL));
@@ -2637,7 +1936,8 @@
 TEST_F(WiFiTimerTest, ReconnectTimer) {
   EXPECT_CALL(mock_dispatcher_, PostTask(_)).Times(AnyNumber());
   EXPECT_CALL(mock_dispatcher_, PostDelayedTask(_, _)).Times(AnyNumber());
-  SetupConnectedService(DBus::Path());
+  StartWiFi();
+  SetupConnectedService(DBus::Path(), NULL, NULL);
   Mock::VerifyAndClearExpectations(&mock_dispatcher_);
 
   EXPECT_CALL(mock_dispatcher_, PostDelayedTask(
@@ -2717,31 +2017,39 @@
   // An EAP failure without a previous TLS indication yields a generic failure.
   Mock::VerifyAndClearExpectations(service.get());
   EXPECT_CALL(*service,
-              SetFailure(Service::kFailureEAPAuthentication)).Times(1);
+              DisconnectWithFailure(Service::kFailureEAPAuthentication, NULL))
+      .Times(1);
   ReportEAPEvent(wpa_supplicant::kEAPStatusCompletion,
                  wpa_supplicant::kEAPParameterFailure);
   Mock::VerifyAndClearExpectations(service.get());
 
   // An EAP failure with a previous TLS indication yields a specific failure.
   SetCurrentService(service);
+  EXPECT_CALL(*service, DisconnectWithFailure(_, _)).Times(0);
   EXPECT_CALL(*service, SetFailure(_)).Times(0);
   ReportEAPEvent(wpa_supplicant::kEAPStatusLocalTLSAlert, string());
   Mock::VerifyAndClearExpectations(service.get());
-  EXPECT_CALL(*service, SetFailure(Service::kFailureEAPLocalTLS)).Times(1);
+  EXPECT_CALL(*service,
+              DisconnectWithFailure(Service::kFailureEAPLocalTLS, NULL))
+      .Times(1);
   ReportEAPEvent(wpa_supplicant::kEAPStatusCompletion,
                  wpa_supplicant::kEAPParameterFailure);
   Mock::VerifyAndClearExpectations(service.get());
 
   SetCurrentService(service);
+  EXPECT_CALL(*service, DisconnectWithFailure(_, _)).Times(0);
   EXPECT_CALL(*service, SetFailure(_)).Times(0);
   ReportEAPEvent(wpa_supplicant::kEAPStatusRemoteTLSAlert, string());
   Mock::VerifyAndClearExpectations(service.get());
-  EXPECT_CALL(*service, SetFailure(Service::kFailureEAPRemoteTLS)).Times(1);
+  EXPECT_CALL(*service,
+              DisconnectWithFailure(Service::kFailureEAPRemoteTLS, NULL))
+      .Times(1);
   ReportEAPEvent(wpa_supplicant::kEAPStatusCompletion,
                  wpa_supplicant::kEAPParameterFailure);
   Mock::VerifyAndClearExpectations(service.get());
 
   SetCurrentService(service);
+  EXPECT_CALL(*service, DisconnectWithFailure(_, _)).Times(0);
   EXPECT_CALL(*service, SetFailure(_)).Times(0);
   const string kStrangeParameter("ennui");
   EXPECT_CALL(log, Log(logging::LOG_ERROR, _,EndsWith(