shill: Complete interaction between DeviceInfo and WiMaxProvider.

WiMaxProvider now creates/registers and destroys/deregisters devices
based on DeviceInfo and RPC callbacks. Also, add missing DeviceInfo
WiMax unit tests, and implement WiMaxService's GetDeviceRpcIdentifier
and GetStorageIdentifier.

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

Change-Id: I9dbf8f785ef824ca5628b96a2e22ec0ef6336b97
Reviewed-on: https://gerrit.chromium.org/gerrit/22807
Commit-Ready: Ben Chan <benchan@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
diff --git a/wimax_provider.cc b/wimax_provider.cc
index b2495a0..86d5eb7 100644
--- a/wimax_provider.cc
+++ b/wimax_provider.cc
@@ -4,22 +4,35 @@
 
 #include "shill/wimax_provider.h"
 
+#include <algorithm>
+
 #include <base/bind.h>
+#include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
 
 #include "shill/dbus_properties_proxy_interface.h"
 #include "shill/error.h"
+#include "shill/manager.h"
 #include "shill/proxy_factory.h"
 #include "shill/scope_logger.h"
+#include "shill/wimax.h"
 #include "shill/wimax_manager_proxy_interface.h"
 
 using base::Bind;
 using base::Unretained;
+using std::find;
+using std::map;
 using std::string;
 using std::vector;
 
 namespace shill {
 
+namespace {
+// TODO(petkov): Add these to chromeos/dbus/service_constants.h.
+const char kWiMaxDevicePathPrefix[] = "/org/chromium/WiMaxManager/Device/";
+const char kWiMaxManagerDevicesProperty[] = "Devices";
+}  // namespace
+
 WiMaxProvider::WiMaxProvider(ControlInterface *control,
                              EventDispatcher *dispatcher,
                              Metrics *metrics,
@@ -54,22 +67,104 @@
   SLOG(WiMax, 2) << __func__;
   properties_proxy_.reset();
   manager_proxy_.reset();
+  DestroyDeadDevices(RpcIdentifiers());
 }
 
-void WiMaxProvider::UpdateDevices(const vector<RpcIdentifier> &/*devices*/) {
+void WiMaxProvider::UpdateDevices(const RpcIdentifiers &devices) {
   SLOG(WiMax, 2) << __func__;
-  // TODO(petkov): Create/register and destroy/unregister WiMax devices as
-  // necessary.
-  NOTIMPLEMENTED();
+  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);
+    }
+  }
 }
 
 void WiMaxProvider::OnPropertiesChanged(
     const string &/*interface*/,
-    const DBusPropertiesMap &/*changed_properties*/,
+    const DBusPropertiesMap &changed_properties,
     const vector<string> &/*invalidated_properties*/) {
   SLOG(WiMax, 2) << __func__;
-  // TODO(petkov): Parse the "Devices" property and invoke UpdateDevices.
-  NOTIMPLEMENTED();
+  RpcIdentifiers devices;
+  if (DBusProperties::GetRpcIdentifiers(
+          changed_properties, kWiMaxManagerDevicesProperty, &devices)) {
+    UpdateDevices(devices);
+  }
+}
+
+void WiMaxProvider::OnDeviceInfoAvailable(const string &link_name) {
+  SLOG(WiMax, 2) << __func__ << "(" << link_name << ")";
+  map<string, string>::iterator find_it = pending_devices_.find(link_name);
+  if (find_it != pending_devices_.end()) {
+    RpcIdentifier path = find_it->second;
+    CreateDevice(link_name, path);
+  }
+}
+
+void WiMaxProvider::CreateDevice(const string &link_name,
+                                 const RpcIdentifier &path) {
+  SLOG(WiMax, 2) << __func__ << "(" << link_name << ", " << path << ")";
+  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 "
+              << link_name;
+    return;
+  }
+  int index = device_info->GetIndex(link_name);
+  if (index < 0) {
+    SLOG(WiMax, 2) << link_name << " pending device info.";
+    // Adds the link to the pending device map, waiting for a notification from
+    // DeviceInfo that it's received information about the device from RTNL.
+    pending_devices_[link_name] = path;
+    return;
+  }
+  ByteString address_bytes;
+  if (!device_info->GetMACAddress(index, &address_bytes)) {
+    LOG(ERROR) << "Unable to create a WiMax device with not MAC address: "
+               << link_name;
+    return;
+  }
+  string address = address_bytes.HexEncode();
+  WiMaxRefPtr device(new WiMax(control_, dispatcher_, metrics_, manager_,
+                               link_name, address, index, path));
+  devices_.push_back(device);
+  device_info->RegisterDevice(device);
+}
+
+void WiMaxProvider::DestroyDeadDevices(const RpcIdentifiers &live_devices) {
+  SLOG(WiMax, 2) << __func__ << "(" << live_devices.size() << ")";
+  for (map<string, string>::iterator it = pending_devices_.begin();
+       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;
+      pending_devices_.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+  for (vector<WiMaxRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ) {
+    if (find(live_devices.begin(), live_devices.end(), (*it)->path()) ==
+        live_devices.end()) {
+      SLOG(WiMax, 2) << "Destroying device: " << (*it)->link_name();
+      manager_->device_info()->DeregisterDevice(*it);
+      it = devices_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+}
+
+string WiMaxProvider::GetLinkName(const RpcIdentifier &path) {
+  if (StartsWithASCII(path, kWiMaxDevicePathPrefix, true)) {
+    return path.substr(strlen(kWiMaxDevicePathPrefix));
+  }
+  LOG(ERROR) << "Unable to determine link name from RPC path: " << path;
+  return string();
 }
 
 }  // namespace shill