shill: wimax: Refactor service management out of WiMax into WiMaxProvider.

This allows WiMAX service management to be independent from the
availability of WiMAX devices.

BUG=chrome-os-partner:9907
TEST=unit tests

Change-Id: I52ad31ed8ef1d271b04bbd2cfc17729a5c112831
Reviewed-on: https://gerrit.chromium.org/gerrit/23511
Commit-Ready: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/wimax_provider.cc b/wimax_provider.cc
index b5f0ad5..1091e74 100644
--- a/wimax_provider.cc
+++ b/wimax_provider.cc
@@ -13,8 +13,10 @@
 #include "shill/error.h"
 #include "shill/key_value_store.h"
 #include "shill/manager.h"
+#include "shill/profile.h"
 #include "shill/proxy_factory.h"
 #include "shill/scope_logger.h"
+#include "shill/store_interface.h"
 #include "shill/wimax.h"
 #include "shill/wimax_manager_proxy_interface.h"
 #include "shill/wimax_service.h"
@@ -23,6 +25,7 @@
 using base::Unretained;
 using std::find;
 using std::map;
+using std::set;
 using std::string;
 using std::vector;
 
@@ -38,9 +41,7 @@
       manager_(manager),
       proxy_factory_(ProxyFactory::GetInstance()) {}
 
-WiMaxProvider::~WiMaxProvider() {
-  Stop();
-}
+WiMaxProvider::~WiMaxProvider() {}
 
 void WiMaxProvider::Start() {
   SLOG(WiMax, 2) << __func__;
@@ -58,18 +59,8 @@
   SLOG(WiMax, 2) << __func__;
   manager_proxy_.reset();
   DestroyDeadDevices(RpcIdentifiers());
-}
-
-void WiMaxProvider::OnDevicesChanged(const RpcIdentifiers &devices) {
-  SLOG(WiMax, 2) << __func__;
-  DestroyDeadDevices(devices);
-  for (RpcIdentifiers::const_iterator it = devices.begin();
-       it != devices.end(); ++it) {
-    string link_name = GetLinkName(*it);
-    if (!link_name.empty()) {
-      CreateDevice(link_name, *it);
-    }
-  }
+  networks_.clear();
+  DestroyAllServices();
 }
 
 void WiMaxProvider::OnDeviceInfoAvailable(const string &link_name) {
@@ -81,15 +72,25 @@
   }
 }
 
+void WiMaxProvider::OnNetworksChanged() {
+  SLOG(WiMax, 2) << __func__;
+  // Collects the set if live networks from all devices.
+  networks_.clear();
+  for (map<string, WiMaxRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    const set<RpcIdentifier> &networks = it->second->networks();
+    networks_.insert(networks.begin(), networks.end());
+  }
+  // Stops dead and starts live services based on the collected set of live
+  // networks.
+  StopDeadServices();
+  StartLiveServices();
+}
+
 WiMaxServiceRefPtr WiMaxProvider::GetService(const KeyValueStore &args,
                                              Error *error) {
   SLOG(WiMax, 2) << __func__;
   CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWimax);
-  if (devices_.empty()) {
-    Error::PopulateAndLog(
-        error, Error::kNotSupported, "No WiMAX device available.");
-    return NULL;
-  }
   WiMaxNetworkId id = args.LookupString(WiMaxService::kNetworkIdProperty, "");
   if (id.empty()) {
     Error::PopulateAndLog(
@@ -102,20 +103,78 @@
         error, Error::kInvalidArguments, "Missing WiMAX service name.");
     return NULL;
   }
-  // Use the first available WiMAX device to construct and register the
-  // service. We may need to consider alternatives if this becomes an issue. For
-  // example, we could refactor service creation and management out of WiMax
-  // into WiMaxProvider.
-  WiMaxRefPtr device = devices_.begin()->second;
-  WiMaxServiceRefPtr service = device->GetService(id, name);
+  WiMaxServiceRefPtr service = GetUniqueService(id, name);
   CHECK(service);
   // Configure the service using the the rest of the passed-in arguments.
   service->Configure(args, error);
   // Start the service if there's a matching live network.
-  device->StartLiveServices();
+  StartLiveServices();
   return service;
 }
 
+void WiMaxProvider::CreateServicesFromProfile(const ProfileRefPtr &profile) {
+  SLOG(WiMax, 2) << __func__;
+  bool created = false;
+  const StoreInterface *storage = profile->GetConstStorage();
+  set<string> groups = storage->GetGroupsWithKey(Service::kStorageType);
+  for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
+    const string &storage_id = *it;
+    string type;
+    if (!storage->GetString(storage_id, Service::kStorageType, &type) ||
+        type != Technology::NameFromIdentifier(Technology::kWiMax)) {
+      continue;
+    }
+    if (FindService(storage_id)) {
+      continue;
+    }
+    WiMaxNetworkId id;
+    if (!storage->GetString(storage_id, WiMaxService::kStorageNetworkId, &id) ||
+        id.empty()) {
+      LOG(ERROR) << "Unable to load network id: " << storage_id;
+      continue;
+    }
+    string name;
+    if (!storage->GetString(storage_id, Service::kStorageName, &name) ||
+        name.empty()) {
+      LOG(ERROR) << "Unable to load service name: " << storage_id;
+      continue;
+    }
+    WiMaxServiceRefPtr service = GetUniqueService(id, name);
+    CHECK(service);
+    if (!profile->ConfigureService(service)) {
+      LOG(ERROR) << "Could not configure service: " << storage_id;
+    }
+    created = true;
+  }
+  if (created) {
+    StartLiveServices();
+  }
+}
+
+WiMaxRefPtr WiMaxProvider::SelectCarrier(const WiMaxServiceRefPtr &service) {
+  SLOG(WiMax, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")";
+  if (devices_.empty()) {
+    LOG(ERROR) << "No WiMAX devices available.";
+    return NULL;
+  }
+  // TODO(petkov): For now, just return the first available device. We need to
+  // be smarter here and select a device that sees |service|'s network.
+  return devices_.begin()->second;
+}
+
+void WiMaxProvider::OnDevicesChanged(const RpcIdentifiers &devices) {
+  SLOG(WiMax, 2) << __func__;
+  DestroyDeadDevices(devices);
+  for (RpcIdentifiers::const_iterator it = devices.begin();
+       it != devices.end(); ++it) {
+    const RpcIdentifier &path = *it;
+    string link_name = GetLinkName(path);
+    if (!link_name.empty()) {
+      CreateDevice(link_name, path);
+    }
+  }
+}
+
 void WiMaxProvider::CreateDevice(const string &link_name,
                                  const RpcIdentifier &path) {
   SLOG(WiMax, 2) << __func__ << "(" << link_name << ", " << path << ")";
@@ -127,7 +186,7 @@
   pending_devices_.erase(link_name);
   DeviceInfo *device_info = manager_->device_info();
   if (device_info->IsDeviceBlackListed(link_name)) {
-    LOG(INFO) << "Do not create WiMax device for blacklisted interface "
+    LOG(INFO) << "WiMAX device not created, interface blacklisted: "
               << link_name;
     return;
   }
@@ -141,7 +200,7 @@
   }
   ByteString address_bytes;
   if (!device_info->GetMACAddress(index, &address_bytes)) {
-    LOG(ERROR) << "Unable to create a WiMax device with no MAC address: "
+    LOG(ERROR) << "Unable to create a WiMAX device with no MAC address: "
                << link_name;
     return;
   }
@@ -150,6 +209,7 @@
                                link_name, address, index, path));
   devices_[link_name] = device;
   device_info->RegisterDevice(device);
+  LOG(INFO) << "Created WiMAX device: " << link_name << " @ " << path;
 }
 
 void WiMaxProvider::DestroyDeadDevices(const RpcIdentifiers &live_devices) {
@@ -158,7 +218,7 @@
        it != pending_devices_.end(); ) {
     if (find(live_devices.begin(), live_devices.end(), it->second) ==
         live_devices.end()) {
-      SLOG(WiMax, 2) << "Forgetting pending device: " << it->second;
+      LOG(INFO) << "Forgetting pending device: " << it->second;
       pending_devices_.erase(it++);
     } else {
       ++it;
@@ -168,7 +228,7 @@
        it != devices_.end(); ) {
     if (find(live_devices.begin(), live_devices.end(), it->second->path()) ==
         live_devices.end()) {
-      SLOG(WiMax, 2) << "Destroying device: " << it->first;
+      LOG(INFO) << "Destroying device: " << it->first;
       manager_->device_info()->DeregisterDevice(it->second);
       devices_.erase(it++);
     } else {
@@ -185,4 +245,106 @@
   return string();
 }
 
+WiMaxServiceRefPtr WiMaxProvider::FindService(const string &storage_id) {
+  SLOG(WiMax, 2) << __func__ << "(" << storage_id << ")";
+  for (vector<WiMaxServiceRefPtr>::const_iterator it = services_.begin();
+       it != services_.end(); ++it) {
+    if ((*it)->GetStorageIdentifier() == storage_id) {
+      return *it;
+    }
+  }
+  return NULL;
+}
+
+WiMaxServiceRefPtr WiMaxProvider::GetUniqueService(const WiMaxNetworkId &id,
+                                                   const string &name) {
+  SLOG(WiMax, 2) << __func__ << "(" << id << ", " << name << ")";
+  string storage_id = WiMaxService::CreateStorageIdentifier(id, name);
+  WiMaxServiceRefPtr service = FindService(storage_id);
+  if (service) {
+    SLOG(WiMax, 2) << "Service already exists.";
+    return service;
+  }
+  service = new WiMaxService(control_, dispatcher_, metrics_, manager_);
+  service->set_network_id(id);
+  service->set_friendly_name(name);
+  service->InitStorageIdentifier();
+  services_.push_back(service);
+  manager_->RegisterService(service);
+  LOG(INFO) << "Registered WiMAX service: " << service->GetStorageIdentifier();
+  return service;
+}
+
+WiMaxServiceRefPtr WiMaxProvider::GetDefaultService(
+    const RpcIdentifier &network) {
+  SLOG(WiMax, 2) << __func__ << "(" << network << ")";
+  scoped_ptr<WiMaxNetworkProxyInterface> proxy(
+      proxy_factory_->CreateWiMaxNetworkProxy(network));
+  Error error;
+  uint32 identifier = proxy->Identifier(&error);
+  if (error.IsFailure()) {
+    return NULL;
+  }
+  string name = proxy->Name(&error);
+  if (error.IsFailure()) {
+    return NULL;
+  }
+  return GetUniqueService(
+      WiMaxService::ConvertIdentifierToNetworkId(identifier), name);
+}
+
+void WiMaxProvider::StartLiveServices() {
+  for (set<RpcIdentifier>::const_iterator it = networks_.begin();
+       it != networks_.end(); ++it) {
+    StartLiveServicesForNetwork(*it);
+  }
+}
+
+void WiMaxProvider::StartLiveServicesForNetwork(const RpcIdentifier &network) {
+  SLOG(WiMax, 2) << __func__ << "(" << network << ")";
+  WiMaxServiceRefPtr default_service = GetDefaultService(network);
+  if (!default_service) {
+    return;
+  }
+  // Start services for this live network identifier.
+  for (vector<WiMaxServiceRefPtr>::iterator it = services_.begin();
+       it != services_.end(); ++it) {
+    const WiMaxServiceRefPtr &service = *it;
+    if (service->network_id() != default_service->network_id() ||
+        service->IsStarted()) {
+      continue;
+    }
+    if (!service->Start(proxy_factory_->CreateWiMaxNetworkProxy(network))) {
+      LOG(ERROR) << "Unable to start service: "
+                 << service->GetStorageIdentifier();
+    }
+  }
+}
+
+void WiMaxProvider::StopDeadServices() {
+  SLOG(WiMax, 2) << __func__ << "(" << networks_.size() << ")";
+  for (vector<WiMaxServiceRefPtr>::iterator it = services_.begin();
+       it != services_.end(); ++it) {
+    const WiMaxServiceRefPtr &service = *it;
+    if (!service->IsStarted() ||
+        ContainsKey(networks_, service->GetNetworkObjectPath())) {
+      continue;
+    }
+    service->Stop();
+  }
+}
+
+void WiMaxProvider::DestroyAllServices() {
+  SLOG(WiMax, 2) << __func__;
+  while (!services_.empty()) {
+    const WiMaxServiceRefPtr &service = services_.back();
+    // Stop the service so that it can notify its carrier device, if any.
+    service->Stop();
+    manager_->DeregisterService(service);
+    LOG(INFO) << "Deregistered WiMAX service: "
+              << service->GetStorageIdentifier();
+    services_.pop_back();
+  }
+}
+
 }  // namespace shill