shill: Support for GSM network scanning.

This is mostly placeholder code since synchronous calls will cause crashes due
to timeouts.

BUG=chromium-os:19805
TEST=unit tests

Change-Id: I7ad07d6274b19b39d7955ee256c6928c9f1ce5a1
Reviewed-on: http://gerrit.chromium.org/gerrit/7895
Commit-Ready: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 53d7c74..9995354 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include <base/logging.h>
+#include <base/string_number_conversions.h>
 #include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
 #include <mm/mm-modem.h>
@@ -30,12 +31,18 @@
 #include "shill/shill_event.h"
 
 using std::make_pair;
+using std::map;
 using std::string;
 using std::vector;
 
 namespace shill {
 
 const char Cellular::kConnectPropertyPhoneNumber[] = "number";
+const char Cellular::kNetworkPropertyAccessTechnology[] = "access-tech";
+const char Cellular::kNetworkPropertyID[] = "operator-num";
+const char Cellular::kNetworkPropertyLongName[] = "operator-long";
+const char Cellular::kNetworkPropertyShortName[] = "operator-short";
+const char Cellular::kNetworkPropertyStatus[] = "status";
 const char Cellular::kPhoneNumberCDMA[] = "#777";
 const char Cellular::kPhoneNumberGSM[] = "*99#";
 
@@ -79,60 +86,6 @@
   return dict_;
 }
 
-Cellular::Network::Network() {
-  dict_[flimflam::kStatusProperty] = "";
-  dict_[flimflam::kNetworkIdProperty] = "";
-  dict_[flimflam::kShortNameProperty] = "";
-  dict_[flimflam::kLongNameProperty] = "";
-  dict_[flimflam::kTechnologyProperty] = "";
-}
-
-Cellular::Network::~Network() {}
-
-const string &Cellular::Network::GetStatus() const {
-  return dict_.find(flimflam::kStatusProperty)->second;
-}
-
-void Cellular::Network::SetStatus(const string &status) {
-  dict_[flimflam::kStatusProperty] = status;
-}
-
-const string &Cellular::Network::GetId() const {
-  return dict_.find(flimflam::kNetworkIdProperty)->second;
-}
-
-void Cellular::Network::SetId(const string &id) {
-  dict_[flimflam::kNetworkIdProperty] = id;
-}
-
-const string &Cellular::Network::GetShortName() const {
-  return dict_.find(flimflam::kShortNameProperty)->second;
-}
-
-void Cellular::Network::SetShortName(const string &name) {
-  dict_[flimflam::kShortNameProperty] = name;
-}
-
-const string &Cellular::Network::GetLongName() const {
-  return dict_.find(flimflam::kLongNameProperty)->second;
-}
-
-void Cellular::Network::SetLongName(const string &name) {
-  dict_[flimflam::kLongNameProperty] = name;
-}
-
-const string &Cellular::Network::GetTechnology() const {
-  return dict_.find(flimflam::kTechnologyProperty)->second;
-}
-
-void Cellular::Network::SetTechnology(const string &technology) {
-  dict_[flimflam::kTechnologyProperty] = technology;
-}
-
-const Stringmap &Cellular::Network::ToDict() const {
-  return dict_;
-}
-
 Cellular::CDMA::CDMA()
     : registration_state_evdo(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
       registration_state_1x(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
@@ -175,6 +128,8 @@
   store->RegisterConstString(flimflam::kEsnProperty, &esn_);
   store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
                              &firmware_revision_);
+  store->RegisterConstStringmaps(flimflam::kFoundNetworksProperty,
+                                 &found_networks_);
   store->RegisterConstString(flimflam::kHardwareRevisionProperty,
                              &hardware_revision_);
   store->RegisterConstStringmap(flimflam::kHomeProviderProperty,
@@ -193,9 +148,6 @@
   HelpRegisterDerivedStrIntPair(flimflam::kSIMLockStatusProperty,
                                 &Cellular::SimLockStatusToProperty,
                                 NULL);
-  HelpRegisterDerivedStringmaps(flimflam::kFoundNetworksProperty,
-                                &Cellular::EnumerateNetworks,
-                                NULL);
 
   store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
   store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
@@ -851,7 +803,96 @@
   }
 }
 
+void Cellular::Scan(Error *error) {
+  VLOG(2) << __func__;
+  if (type_ != kTypeGSM) {
+    const string kMessage = "Network scanning support for GSM only.";
+    LOG(ERROR) << kMessage;
+    CHECK(error);
+    error->Populate(Error::kNotSupported, kMessage);
+    return;
+  }
+  // Defer scan because we may be in a dbus-c++ callback.
+  dispatcher()->PostTask(
+      task_factory_.NewRunnableMethod(&Cellular::ScanTask));
+}
+
+void Cellular::ScanTask() {
+  VLOG(2) << __func__;
+  // TODO(petkov): Defer scan requests if a scan is in progress already.
+  CHECK_EQ(kTypeGSM, type_);
+  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583). This is a
+  // must for this call which is basically a stub at this point.
+  ModemGSMNetworkProxyInterface::ScanResults results =
+      gsm_network_proxy_->Scan();
+  found_networks_.clear();
+  for (ModemGSMNetworkProxyInterface::ScanResults::const_iterator it =
+           results.begin(); it != results.end(); ++it) {
+    found_networks_.push_back(ParseScanResult(*it));
+  }
+}
+
+Stringmap Cellular::ParseScanResult(
+    const ModemGSMNetworkProxyInterface::ScanResult &result) {
+  Stringmap parsed;
+  for (ModemGSMNetworkProxyInterface::ScanResult::const_iterator it =
+           result.begin(); it != result.end(); ++it) {
+    // TODO(petkov): Define these in system_api/service_constants.h. The
+    // numerical values are taken from 3GPP TS 27.007 Section 7.3.
+    static const char * const kStatusString[] = {
+      "unknown",
+      "available",
+      "current",
+      "forbidden",
+    };
+    // TODO(petkov): Do we need the finer level of granularity here or can we
+    // use the same granularity as GetNetworkTechnologyString (e.g.,
+    // HSDPA->"HSPA").
+    static const char * const kTechnologyString[] = {
+      flimflam::kNetworkTechnologyGsm,
+      "GSM Compact",
+      flimflam::kNetworkTechnologyUmts,
+      flimflam::kNetworkTechnologyEdge,
+      "HSDPA",
+      "HSUPA",
+      flimflam::kNetworkTechnologyHspa,
+    };
+    VLOG(2) << "Network property: " << it->first << " = " << it->second;
+    if (it->first == kNetworkPropertyStatus) {
+      int status = 0;
+      if (base::StringToInt(it->second, &status) &&
+          status >= 0 &&
+          status < static_cast<int>(arraysize(kStatusString))) {
+        parsed[flimflam::kStatusProperty] = kStatusString[status];
+      } else {
+        LOG(ERROR) << "Unexpected status value: " << it->second;
+      }
+    } else if (it->first == kNetworkPropertyID) {
+      parsed[flimflam::kNetworkIdProperty] = it->second;
+    } else if (it->first == kNetworkPropertyLongName) {
+      parsed[flimflam::kLongNameProperty] = it->second;
+    } else if (it->first == kNetworkPropertyShortName) {
+      parsed[flimflam::kShortNameProperty] = it->second;
+    } else if (it->first == kNetworkPropertyAccessTechnology) {
+      int tech = 0;
+      if (base::StringToInt(it->second, &tech) &&
+          tech >= 0 &&
+          tech < static_cast<int>(arraysize(kTechnologyString))) {
+        parsed[flimflam::kTechnologyProperty] = kTechnologyString[tech];
+      } else {
+        LOG(ERROR) << "Unexpected technology value: " << it->second;
+      }
+    } else {
+      LOG(WARNING) << "Unknown network property ignored: " << it->first;
+    }
+  }
+  // TODO(petkov): If long name is not set and there's a network ID, do a mobile
+  // database lookup (crosbug.com/19699).
+  return parsed;
+}
+
 void Cellular::Activate(const string &carrier, Error *error) {
+  VLOG(2) << __func__ << "(" << carrier << ")";
   if (type_ != kTypeCDMA) {
     const string kMessage = "Unable to activate non-CDMA modem.";
     LOG(ERROR) << kMessage;
@@ -874,6 +915,7 @@
 
 void Cellular::ActivateTask(const string &carrier) {
   VLOG(2) << __func__ << "(" << carrier << ")";
+  CHECK_EQ(kTypeCDMA, type_);
   if (state_ != kStateEnabled &&
       state_ != kStateRegistered) {
     LOG(ERROR) << "Unable to activate in " << GetStateString(state_);
@@ -952,16 +994,6 @@
   NOTIMPLEMENTED();
 }
 
-Stringmaps Cellular::EnumerateNetworks() {
-  Stringmaps to_return;
-  for (vector<Network>::const_iterator it = found_networks_.begin();
-       it != found_networks_.end();
-       ++it) {
-    to_return.push_back(it->ToDict());
-  }
-  return to_return;
-}
-
 void Cellular::SetGSMAccessTechnology(uint32 access_technology) {
   CHECK_EQ(kTypeGSM, type_);
   gsm_.access_technology = access_technology;
@@ -977,16 +1009,6 @@
                               sim_lock_status_.retries_left));
 }
 
-void Cellular::HelpRegisterDerivedStringmaps(
-    const string &name,
-    Stringmaps(Cellular::*get)(void),
-    bool(Cellular::*set)(const Stringmaps&)) {
-  mutable_store()->RegisterDerivedStringmaps(
-      name,
-      StringmapsAccessor(
-          new CustomAccessor<Cellular, Stringmaps>(this, get, set)));
-}
-
 void Cellular::HelpRegisterDerivedStrIntPair(
     const string &name,
     StrIntPair(Cellular::*get)(void),
diff --git a/cellular.h b/cellular.h
index 900536e..0046238 100644
--- a/cellular.h
+++ b/cellular.h
@@ -91,34 +91,6 @@
     DISALLOW_COPY_AND_ASSIGN(Operator);
   };
 
-  class Network {
-   public:
-    Network();
-    ~Network();
-
-    const std::string &GetStatus() const;
-    void SetStatus(const std::string &status);
-
-    const std::string &GetId() const;
-    void SetId(const std::string &id);
-
-    const std::string &GetShortName() const;
-    void SetShortName(const std::string &name);
-
-    const std::string &GetLongName() const;
-    void SetLongName(const std::string &name);
-
-    const std::string &GetTechnology() const;
-    void SetTechnology(const std::string &technology);
-
-    const Stringmap &ToDict() const;
-
-   private:
-    Stringmap dict_;
-
-    DISALLOW_COPY_AND_ASSIGN(Network);
-  };
-
   struct SimLockStatus {
    public:
     SimLockStatus() : retries_left(0) {}
@@ -167,6 +139,7 @@
   virtual void Stop();
   virtual bool TechnologyIs(Technology type) const;
   virtual void LinkEvent(unsigned int flags, unsigned int change);
+  virtual void Scan(Error *error);
   virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
   virtual void RequirePIN(const std::string &pin, bool require, Error *error);
   virtual void EnterPIN(const std::string &pin, Error *error);
@@ -203,11 +176,13 @@
   FRIEND_TEST(CellularTest, EnterPINError);
   FRIEND_TEST(CellularTest, InitProxiesCDMA);
   FRIEND_TEST(CellularTest, InitProxiesGSM);
+  FRIEND_TEST(CellularTest, ParseScanResult);
   FRIEND_TEST(CellularTest, RegisterOnNetwork);
   FRIEND_TEST(CellularTest, RegisterOnNetworkError);
   FRIEND_TEST(CellularTest, RequirePIN);
   FRIEND_TEST(CellularTest, RequirePINError);
   FRIEND_TEST(CellularTest, SetGSMAccessTechnology);
+  FRIEND_TEST(CellularTest, Scan);
   FRIEND_TEST(CellularTest, StartConnected);
   FRIEND_TEST(CellularTest, StartCDMARegister);
   FRIEND_TEST(CellularTest, StartGSMRegister);
@@ -237,12 +212,18 @@
     std::string spn;
   };
 
+  static const char kNetworkPropertyAccessTechnology[];
+  static const char kNetworkPropertyID[];
+  static const char kNetworkPropertyLongName[];
+  static const char kNetworkPropertyShortName[];
+  static const char kNetworkPropertyStatus[];
   static const char kPhoneNumberCDMA[];
   static const char kPhoneNumberGSM[];
 
   void SetState(State state);
 
   void ConnectTask(const DBusPropertiesMap &properties);
+  void ScanTask();
   void ActivateTask(const std::string &carrier);
   void RegisterOnNetworkTask(const std::string &network_id);
   void RequirePINTask(const std::string &pin, bool require);
@@ -256,7 +237,6 @@
 
   StrIntPair SimLockStatusToProperty();
 
-  Stringmaps EnumerateNetworks();
   void HelpRegisterDerivedStringmaps(const std::string &name,
                                      Stringmaps(Cellular::*get)(void),
                                      bool(Cellular::*set)(const Stringmaps&));
@@ -310,6 +290,9 @@
 
   void HandleNewCDMAActivationState(uint32 error);
 
+  Stringmap ParseScanResult(
+      const ModemGSMNetworkProxyInterface::ScanResult &result);
+
   // Signal callbacks inherited from ModemCDMAProxyListener.
   virtual void OnCDMAActivationStateChanged(
       uint32 activation_state,
@@ -366,7 +349,7 @@
   bool scanning_;
   uint16 scan_interval_;
   std::string selected_network_;
-  std::vector<Network> found_networks_;
+  Stringmaps found_networks_;
   SimLockStatus sim_lock_status_;
   Operator home_provider_;
 
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index d43c2fd..1e9f63a 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -28,6 +28,7 @@
 #include "shill/proxy_factory.h"
 #include "shill/shill_event.h"
 
+using std::map;
 using std::string;
 using testing::_;
 using testing::NiceMock;
@@ -794,6 +795,48 @@
   dispatcher_.DispatchPendingEvents();
 }
 
+TEST_F(CellularTest, Scan) {
+  static const char kID0[] = "123";
+  static const char kID1[] = "456";
+  Error error;
+  device_->type_ = Cellular::kTypeGSM;
+  device_->Scan(&error);
+  EXPECT_TRUE(error.IsSuccess());
+  ModemGSMNetworkProxyInterface::ScanResults results;
+  results.push_back(ModemGSMNetworkProxyInterface::ScanResult());
+  results[0][Cellular::kNetworkPropertyID] = kID0;
+  results.push_back(ModemGSMNetworkProxyInterface::ScanResult());
+  results[1][Cellular::kNetworkPropertyID] = kID1;
+  EXPECT_CALL(*gsm_network_proxy_, Scan()).WillOnce(Return(results));
+  device_->gsm_network_proxy_.reset(gsm_network_proxy_.release());
+  device_->found_networks_.resize(2, Stringmap());
+  dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(2, device_->found_networks_.size());
+  EXPECT_EQ(kID0, device_->found_networks_[0][flimflam::kNetworkIdProperty]);
+  EXPECT_EQ(kID1, device_->found_networks_[1][flimflam::kNetworkIdProperty]);
+}
+
+TEST_F(CellularTest, ParseScanResult) {
+  static const char kID[] = "123";
+  static const char kLongName[] = "long name";
+  static const char kShortName[] = "short name";
+  ModemGSMNetworkProxyInterface::ScanResult result;
+  result[Cellular::kNetworkPropertyStatus] = "1";
+  result[Cellular::kNetworkPropertyID] = kID;
+  result[Cellular::kNetworkPropertyLongName] = kLongName;
+  result[Cellular::kNetworkPropertyShortName] = kShortName;
+  result[Cellular::kNetworkPropertyAccessTechnology] = "3";
+  result["unknown property"] = "random value";
+  Stringmap parsed = device_->ParseScanResult(result);
+  EXPECT_EQ(5, parsed.size());
+  EXPECT_EQ("available", parsed[flimflam::kStatusProperty]);
+  EXPECT_EQ(kID, parsed[flimflam::kNetworkIdProperty]);
+  EXPECT_EQ(kLongName, parsed[flimflam::kLongNameProperty]);
+  EXPECT_EQ(kShortName, parsed[flimflam::kShortNameProperty]);
+  EXPECT_EQ(flimflam::kNetworkTechnologyEdge,
+            parsed[flimflam::kTechnologyProperty]);
+}
+
 TEST_F(CellularTest, Activate) {
   Error error;
   device_->type_ = Cellular::kTypeCDMA;
diff --git a/device.cc b/device.cc
index 13426df..1a16245 100644
--- a/device.cc
+++ b/device.cc
@@ -146,8 +146,12 @@
           << std::dec << std::noshowbase;
 }
 
-void Device::Scan() {
+void Device::Scan(Error *error) {
   VLOG(2) << "Device " << link_name_ << " scan requested.";
+  const string kMessage = "Device doesn't support scan.";
+  LOG(ERROR) << kMessage;
+  CHECK(error);
+  error->Populate(Error::kNotSupported, kMessage);
 }
 
 void Device::RegisterOnNetwork(const std::string &network_id, Error *error) {
diff --git a/device.h b/device.h
index 22dd9a1..0ab72a9 100644
--- a/device.h
+++ b/device.h
@@ -63,11 +63,11 @@
   virtual bool TechnologyIs(const Technology type) const;
 
   virtual void LinkEvent(unsigned flags, unsigned change);
-  virtual void Scan();
 
   virtual void ConfigIP() {}
 
   // The default implementation sets |error| to kNotSupported.
+  virtual void Scan(Error *error);
   virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
   virtual void RequirePIN(const std::string &pin, bool require, Error *error);
   virtual void EnterPIN(const std::string &pin, Error *error);
diff --git a/device_dbus_adaptor.cc b/device_dbus_adaptor.cc
index 1b640c2..521ef9f 100644
--- a/device_dbus_adaptor.cc
+++ b/device_dbus_adaptor.cc
@@ -75,6 +75,9 @@
 }
 
 void DeviceDBusAdaptor::ProposeScan(::DBus::Error &error) {
+  Error e;
+  device_->Scan(&e);
+  e.ToDBusError(&error);
 }
 
 ::DBus::Path DeviceDBusAdaptor::AddIPConfig(const string& ,
diff --git a/manager.cc b/manager.cc
index 960eaac..996c291 100644
--- a/manager.cc
+++ b/manager.cc
@@ -312,7 +312,7 @@
     for (vector<DeviceRefPtr>::iterator it = wifi_devices.begin();
          it != wifi_devices.end();
          ++it) {
-      (*it)->Scan();
+      (*it)->Scan(error);
     }
   } else {
     // TODO(quiche): support scanning for other technologies?
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 28286d6..960fddb 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -302,10 +302,10 @@
     manager()->RegisterDevice(mock_device2_.get());
     EXPECT_CALL(*mock_device_, TechnologyIs(Device::kWifi))
         .WillRepeatedly(Return(true));
-    EXPECT_CALL(*mock_device_, Scan());
+    EXPECT_CALL(*mock_device_, Scan(_));
     EXPECT_CALL(*mock_device2_, TechnologyIs(Device::kWifi))
         .WillRepeatedly(Return(false));
-    EXPECT_CALL(*mock_device2_, Scan()).Times(0);
+    EXPECT_CALL(*mock_device2_, Scan(_)).Times(0);
     manager()->RequestScan(flimflam::kTypeWifi, &error);
   }
 
diff --git a/mock_device.h b/mock_device.h
index fd73d6c..4c3fa9b 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -29,7 +29,7 @@
 
   MOCK_METHOD0(Start, void());
   MOCK_METHOD0(Stop, void());
-  MOCK_METHOD0(Scan, void());
+  MOCK_METHOD1(Scan, void(Error *error));
   MOCK_CONST_METHOD1(TechnologyIs, bool(const Technology technology));
 
  private:
diff --git a/mock_modem_gsm_network_proxy.h b/mock_modem_gsm_network_proxy.h
index bec2180..219ace1 100644
--- a/mock_modem_gsm_network_proxy.h
+++ b/mock_modem_gsm_network_proxy.h
@@ -20,6 +20,7 @@
   MOCK_METHOD0(GetRegistrationInfo, RegistrationInfo());
   MOCK_METHOD0(GetSignalQuality, uint32());
   MOCK_METHOD1(Register, void(const std::string &network_id));
+  MOCK_METHOD0(Scan, ScanResults());
   MOCK_METHOD0(AccessTechnology, uint32());
 
  private:
diff --git a/modem_gsm_network_proxy.cc b/modem_gsm_network_proxy.cc
index ba1d362..02bb4d3 100644
--- a/modem_gsm_network_proxy.cc
+++ b/modem_gsm_network_proxy.cc
@@ -32,6 +32,10 @@
   proxy_.Register(network_id);
 }
 
+ModemGSMNetworkProxyInterface::ScanResults ModemGSMNetworkProxy::Scan() {
+  return proxy_.Scan();
+}
+
 uint32 ModemGSMNetworkProxy::AccessTechnology() {
   return proxy_.AccessTechnology();
 }
diff --git a/modem_gsm_network_proxy.h b/modem_gsm_network_proxy.h
index 7a8bddd..b23e011 100644
--- a/modem_gsm_network_proxy.h
+++ b/modem_gsm_network_proxy.h
@@ -24,6 +24,7 @@
   virtual RegistrationInfo GetRegistrationInfo();
   virtual uint32 GetSignalQuality();
   virtual void Register(const std::string &network_id);
+  virtual ScanResults Scan();
   virtual uint32 AccessTechnology();
 
  private:
diff --git a/modem_gsm_network_proxy_interface.h b/modem_gsm_network_proxy_interface.h
index 0c53f95..84299f5 100644
--- a/modem_gsm_network_proxy_interface.h
+++ b/modem_gsm_network_proxy_interface.h
@@ -5,7 +5,9 @@
 #ifndef SHILL_MODEM_GSM_NETWORK_PROXY_INTERFACE_
 #define SHILL_MODEM_GSM_NETWORK_PROXY_INTERFACE_
 
+#include <map>
 #include <string>
+#include <vector>
 
 #include <base/basictypes.h>
 #include <dbus-c++/types.h>
@@ -17,12 +19,15 @@
 class ModemGSMNetworkProxyInterface {
  public:
   typedef DBus::Struct<uint32, std::string, std::string> RegistrationInfo;
+  typedef std::map<std::string, std::string> ScanResult;
+  typedef std::vector<ScanResult> ScanResults;
 
   virtual ~ModemGSMNetworkProxyInterface() {}
 
   virtual RegistrationInfo GetRegistrationInfo() = 0;
   virtual uint32 GetSignalQuality() = 0;
   virtual void Register(const std::string &network_id) = 0;
+  virtual ScanResults Scan() = 0;
 
   // Properties.
   virtual uint32 AccessTechnology() = 0;
diff --git a/property_store.cc b/property_store.cc
index d2e7454..9fb61ba 100644
--- a/property_store.cc
+++ b/property_store.cc
@@ -229,6 +229,17 @@
       StringmapAccessor(new ConstPropertyAccessor<Stringmap>(prop));
 }
 
+void PropertyStore::RegisterStringmaps(const string &name, Stringmaps *prop) {
+  stringmaps_properties_[name] =
+      StringmapsAccessor(new PropertyAccessor<Stringmaps>(prop));
+}
+
+void PropertyStore::RegisterConstStringmaps(const string &name,
+                                            const Stringmaps *prop) {
+  stringmaps_properties_[name] =
+      StringmapsAccessor(new ConstPropertyAccessor<Stringmaps>(prop));
+}
+
 void PropertyStore::RegisterStrings(const string &name, Strings *prop) {
   strings_properties_[name] =
       StringsAccessor(new PropertyAccessor<Strings>(prop));
diff --git a/property_store.h b/property_store.h
index 9c82450..2edc333 100644
--- a/property_store.h
+++ b/property_store.h
@@ -91,6 +91,8 @@
   void RegisterConstString(const std::string &name, const std::string *prop);
   void RegisterStringmap(const std::string &name, Stringmap *prop);
   void RegisterConstStringmap(const std::string &name, const Stringmap *prop);
+  void RegisterStringmaps(const std::string &name, Stringmaps *prop);
+  void RegisterConstStringmaps(const std::string &name, const Stringmaps *prop);
   void RegisterStrings(const std::string &name, Strings *prop);
   void RegisterConstStrings(const std::string &name, const Strings *prop);
   void RegisterUint8(const std::string &name, uint8 *prop);
diff --git a/wifi.cc b/wifi.cc
index 43af963..dd2acb9 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -120,7 +120,7 @@
   // all BSSes (not just new ones since the last scan).
   supplicant_interface_proxy_->FlushBSS(0);
 
-  Scan();
+  Scan(NULL);
   Device::Start();
 }
 
@@ -154,7 +154,7 @@
           << " ServiceMap entries.";
 }
 
-void WiFi::Scan() {
+void WiFi::Scan(Error *error) {
   LOG(INFO) << __func__;
 
   // needs to send a D-Bus message, but may be called from D-Bus
diff --git a/wifi.h b/wifi.h
index 8128965..04b1d09 100644
--- a/wifi.h
+++ b/wifi.h
@@ -31,9 +31,10 @@
        const std::string &address,
        int interface_index);
   virtual ~WiFi();
+
   virtual void Start();
   virtual void Stop();
-  virtual void Scan();
+  virtual void Scan(Error *error);
   virtual bool TechnologyIs(const Technology type) const;
   virtual void LinkEvent(unsigned int flags, unsigned int change);