shill: track wpa_supplicant state, and use it
to update Service state

BUG=chromium-os:22594
TEST=unittests, manual

Manual testing: tried network_WiFiRoaming.002Suspend,
but this still fails. The reason is that we ignore
transitions into disconnected state. I'll address that
in a separate patch, which will add tracking of
CurrentBSS.

Bonus changes bundled in this commit:
- fix some comments (capitalization/punctuation,
  XXX -> TODO)
- WiFiMainTest::InitateConnect arg changed from bare pointer
  to refptr

Change-Id: I6c42f9794c8742fa2b46d03a54d77e0545c899c5
Reviewed-on: https://gerrit.chromium.org/gerrit/11460
Reviewed-by: Gaurav Shah <gauravsh@chromium.org>
Commit-Ready: mukesh agrawal <quiche@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/Makefile b/Makefile
index a572a1d..a12814b 100644
--- a/Makefile
+++ b/Makefile
@@ -202,6 +202,7 @@
 	mock_supplicant_process_proxy.o \
 	mock_time.o \
 	mock_wifi.o \
+	mock_wifi_service.o \
 	modem_info_unittest.o \
 	modem_manager_unittest.o \
 	modem_unittest.o \
diff --git a/mock_wifi_service.cc b/mock_wifi_service.cc
new file mode 100644
index 0000000..9dd6e5b
--- /dev/null
+++ b/mock_wifi_service.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 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_service.h"
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+class ControlInterface;
+class EventDispatcher;
+class Manager;
+
+MockWiFiService::MockWiFiService(ControlInterface *control_interface,
+                                 EventDispatcher *dispatcher,
+                                 Manager *manager,
+                                 const WiFiRefPtr &device,
+                                 const vector<uint8_t> &ssid,
+                                 const string &mode,
+                                 const string &security,
+                                 bool hidden_ssid)
+    : WiFiService(
+        control_interface, dispatcher, manager, device, ssid, mode, security,
+        hidden_ssid) {}
+
+MockWiFiService::~MockWiFiService() {}
+
+}  // namespace shill
diff --git a/mock_wifi_service.h b/mock_wifi_service.h
new file mode 100644
index 0000000..a0d4ace
--- /dev/null
+++ b/mock_wifi_service.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 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_SERVICE_
+#define SHILL_MOCK_WIFI_SERVICE_
+
+#include <gmock/gmock.h>
+
+#include "shill/wifi_service.h"
+
+namespace shill {
+
+class MockWiFiService : public WiFiService {
+ public:
+  MockWiFiService(ControlInterface *control_interface,
+                  EventDispatcher *dispatcher,
+                  Manager *manager,
+                  const WiFiRefPtr &device,
+                  const std::vector<uint8_t> &ssid,
+                  const std::string &mode,
+                  const std::string &security,
+                  bool hidden_ssid);
+  virtual ~MockWiFiService();
+
+  MOCK_METHOD1(SetState, void(ConnectState state));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockWiFiService);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_WIFI_SERVICE_
diff --git a/service.cc b/service.cc
index 97cb2f4..2131ce6 100644
--- a/service.cc
+++ b/service.cc
@@ -178,6 +178,10 @@
 }
 
 void Service::SetState(ConnectState state) {
+  // TODO(quiche): Print string, rather than enum.
+  LOG(INFO) << friendly_name_ << " " << __func__ << " "
+            << state_ << " -> " << state;
+
   if (state == state_) {
     return;
   }
diff --git a/supplicant_interface_proxy.cc b/supplicant_interface_proxy.cc
index ebc5db0..6d80432 100644
--- a/supplicant_interface_proxy.cc
+++ b/supplicant_interface_proxy.cc
@@ -100,9 +100,9 @@
 }
 
 void SupplicantInterfaceProxy::Proxy::PropertiesChanged(
-    const std::map<string, ::DBus::Variant> &/*properties*/) {
+    const std::map<string, ::DBus::Variant> &properties) {
   LOG(INFO) << __func__;
-  // XXX
+  wifi_->PropertiesChanged(properties);
 }
 
 void SupplicantInterfaceProxy::Proxy::ScanDone(const bool& success) {
diff --git a/wifi.cc b/wifi.cc
index 336d21b..01d9613 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -59,6 +59,7 @@
     "service type is unsupported";
 const char WiFi::kManagerErrorUnsupportedServiceMode[] =
     "service mode is unsupported";
+const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
 
 // NB: we assume supplicant is already running. [quiche.20110518]
 WiFi::WiFi(ControlInterface *control_interface,
@@ -79,7 +80,8 @@
       bgscan_signal_threshold_(0),
       scan_pending_(false),
       scan_interval_(0),
-      link_up_(false) {
+      link_up_(false),
+      supplicant_state_(kInterfaceStateUnknown) {
   PropertyStore *store = this->mutable_store();
   store->RegisterString(flimflam::kBgscanMethodProperty, &bgscan_method_);
   store->RegisterUint16(flimflam::kBgscanShortIntervalProperty,
@@ -118,9 +120,9 @@
     if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
       interface_path =
           supplicant_process_proxy_->GetInterface(link_name());
-      // XXX crash here, if device missing?
+      // TODO(quiche): Is it okay to crash here, if device is missing?
     } else {
-      // XXX
+      // TODO(quiche): Log error.
     }
   }
 
@@ -128,13 +130,13 @@
       proxy_factory_->CreateSupplicantInterfaceProxy(
           this, interface_path, wpa_supplicant::kDBusAddr));
 
-  // TODO(quiche) set ApScan=1 and BSSExpireAge=190, like flimflam does?
+  // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
 
-  // clear out any networks that might previously have been configured
+  // Clear out any networks that might previously have been configured
   // for this interface.
   supplicant_interface_proxy_->RemoveAllNetworks();
 
-  // flush interface's BSS cache, so that we get BSSAdded signals for
+  // Flush interface's BSS cache, so that we get BSSAdded signals for
   // all BSSes (not just new ones since the last scan).
   supplicant_interface_proxy_->FlushBSS(0);
 
@@ -144,7 +146,7 @@
 
 void WiFi::Stop() {
   VLOG(2) << "WiFi " << link_name() << " stopping.";
-  // TODO(quiche): remove interface from supplicant
+  // TODO(quiche): Remove interface from supplicant.
   supplicant_interface_proxy_.reset();  // breaks a reference cycle
   supplicant_process_proxy_.reset();
   endpoint_by_bssid_.clear();
@@ -156,9 +158,10 @@
     manager()->DeregisterService(*it);
   }
   services_.clear();                  // breaks reference cycles
+  pending_service_ = NULL;            // breaks a reference cycle
 
   Device::Stop();
-  // XXX anything else to do?
+  // TODO(quiche): Anything else to do?
 
   VLOG(3) << "WiFi " << link_name() << " task_factory_ "
           << (task_factory_.empty() ? "is empty." : "is not empty.");
@@ -166,6 +169,8 @@
           << (supplicant_process_proxy_.get() ? "is set." : "is not set.");
   VLOG(3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
           << (supplicant_interface_proxy_.get() ? "is set." : "is not set.");
+  VLOG(3) << "WiFi " << link_name() << " pending_service_ "
+          << (pending_service_.get() ? "is set." : "is not set.");
   VLOG(3) << "WiFi " << link_name() << " has " << endpoint_by_bssid_.size()
           << " EndpointMap entries.";
   VLOG(3) << "WiFi " << link_name() << " has " << service_by_private_id_.size()
@@ -175,8 +180,8 @@
 void WiFi::Scan(Error */*error*/) {
   LOG(INFO) << __func__;
 
-  // needs to send a D-Bus message, but may be called from D-Bus
-  // signal handler context (via Manager::RequestScan). so defer work
+  // Needs to send a D-Bus message, but may be called from D-Bus
+  // signal handler context (via Manager::RequestScan). So defer work
   // to event loop.
   dispatcher()->PostTask(
       task_factory_.NewRunnableMethod(&WiFi::ScanTask));
@@ -187,8 +192,8 @@
 }
 
 void WiFi::LinkEvent(unsigned int flags, unsigned int change) {
-  // TODO(quiche): figure out how to relate these events to supplicant
-  // events. e.g., may be we can ignore LinkEvent, in favor of events
+  // TODO(quiche): Figure out how to relate these events to supplicant
+  // events. E.g., may be we can ignore LinkEvent, in favor of events
   // from SupplicantInterfaceProxy?
   Device::LinkEvent(flags, change);
   if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
@@ -202,7 +207,7 @@
   } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
     LOG(INFO) << link_name() << " is down";
     link_up_ = false;
-    // TODO(quiche): attempt to reconnect to current SSID, another SSID,
+    // TODO(quiche): Attempt to reconnect to current SSID, another SSID,
     // or initiate a scan.
   }
 }
@@ -210,23 +215,31 @@
 void WiFi::BSSAdded(
     const ::DBus::Path &/*BSS*/,
     const std::map<string, ::DBus::Variant> &properties) {
-  // TODO(quiche): write test to verify correct behavior in the case
+  // TODO(quiche): Write test to verify correct behavior in the case
   // where we get multiple BSSAdded events for a single endpoint.
-  // (old Endpoint's refcount should fall to zero, and old Endpoint
-  // should be destroyed)
+  // (Old Endpoint's refcount should fall to zero, and old Endpoint
+  // should be destroyed.)
   //
-  // note: we assume that BSSIDs are unique across endpoints. this
+  // Note: we assume that BSSIDs are unique across endpoints. This
   // means that if an AP reuses the same BSSID for multiple SSIDs, we
   // lose.
   WiFiEndpointRefPtr endpoint(new WiFiEndpoint(properties));
   endpoint_by_bssid_[endpoint->bssid_hex()] = endpoint;
 }
 
+void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
+  // TODO(quiche): Handle changes in other properties.
+  if (ContainsKey(properties, wpa_supplicant::kInterfacePropertyState)) {
+    StateChanged(properties.find(wpa_supplicant::kInterfacePropertyState)->
+                 second.reader().get_string());
+  }
+}
+
 void WiFi::ScanDone() {
   LOG(INFO) << __func__;
 
-  // defer handling of scan result processing, because that processing
-  // may require the the registration of new D-Bus objects. and such
+  // Defer handling of scan result processing, because that processing
+  // may require the the registration of new D-Bus objects. And such
   // registration can't be done in the context of a D-Bus signal
   // handler.
   dispatcher()->PostTask(
@@ -237,9 +250,9 @@
                      const map<string, DBus::Variant> &service_params) {
   DBus::Path network_path;
 
-  // TODO(quiche): handle cases where already connected
+  // TODO(quiche): Handle cases where already connected.
 
-  // TODO(quiche): set scan_ssid=1 in service_params, like flimflam does?
+  // TODO(quiche): Set scan_ssid=1 in service_params, like flimflam does?
   try {
     network_path =
         supplicant_interface_proxy_->AddNetwork(service_params);
@@ -249,15 +262,16 @@
   }
 
   supplicant_interface_proxy_->SelectNetwork(network_path);
-  // XXX add to favorite networks list?
+  // TODO(quiche): Add to favorite networks list?
 
   // SelectService here (instead of in LinkEvent, like Ethernet), so
   // that, if we fail to bring up L2, we can attribute failure correctly.
   //
-  // TODO(quiche): when we add code for dealing with connection failures,
+  // TODO(quiche): When we add code for dealing with connection failures,
   // reconsider if this is the right place to change the selected service.
   // see discussion in crosbug.com/20191.
   SelectService(service);
+  pending_service_ = service;
 }
 
 WiFiServiceRefPtr WiFi::FindService(const std::vector<uint8_t> &ssid,
@@ -306,7 +320,7 @@
 
   scan_pending_ = false;
 
-  // TODO(quiche): group endpoints into services, instead of creating
+  // TODO(quiche): Group endpoints into services, instead of creating
   // a service for every endpoint.
   for (EndpointMap::iterator i(endpoint_by_bssid_.begin());
        i != endpoint_by_bssid_.end(); ++i) {
@@ -341,7 +355,7 @@
     }
   }
 
-  // TODO(quiche): unregister removed services from manager
+  // TODO(quiche): Unregister removed services from manager.
 }
 
 void WiFi::ScanTask() {
@@ -361,7 +375,45 @@
   scan_pending_ = true;
 }
 
-// used by manager, via static WiFi::GetService method
+void WiFi::StateChanged(const string &new_state) {
+  const string &old_state = supplicant_state_;
+
+  LOG(INFO) << link_name() << " " << __func__ << " "
+            << old_state << " -> " << new_state;
+
+  if (pending_service_.get()) {
+    if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
+      // TODO(quiche): Check if we have a race with LinkEvent and/or
+      // IPConfigUpdatedCallback here.
+
+      // After 802.11 negotiation is Completed, we start Configuring
+      // IP connectivity.
+      pending_service_->SetState(Service::kStateConfiguring);
+    } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
+      pending_service_->SetState(Service::kStateAssociating);
+    } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
+               new_state == wpa_supplicant::kInterfaceStateAssociating ||
+               new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
+               new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
+      // Ignore transitions into these states from Completed, to avoid
+      // bothering the user when roaming, or re-keying.
+      if (old_state != wpa_supplicant::kInterfaceStateCompleted)
+        pending_service_->SetState(Service::kStateAssociating);
+    } else {
+      // Other transitions do not affect Service state.
+      //
+      // Note in particular that we ignore a State change into
+      // kInterfaceStateDisconnected, in favor of observing the corresponding
+      // change in CurrentBSS.
+      //
+      // TODO(quiche): Actually implement tracking of CurrentBSS.
+    }
+  }
+
+  supplicant_state_ = new_state;
+}
+
+// Used by Manager.
 WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
   if (!args.ContainsString(flimflam::kTypeProperty)) {
     error->Populate(Error::kInvalidArguments, kManagerErrorTypeRequired);
@@ -446,8 +498,8 @@
                               security_method,
                               hidden_ssid);
     services_.push_back(service);
-    // TODO(quiche): add to service_by_private_id_?
-    // TODO(quiche): register service with manager
+    // TODO(quiche): Add to |service_by_private_id_|?
+    // TODO(quiche): Register |service| with Manager.
   }
 
   if (security_method == flimflam::kSecurityWep ||
@@ -461,7 +513,7 @@
     }
   }
 
-  // TODO(quiche): apply any other configuration parameters
+  // TODO(quiche): Apply any other configuration parameters.
 
   return service;
 }
diff --git a/wifi.h b/wifi.h
index 6965560..fd1cedf 100644
--- a/wifi.h
+++ b/wifi.h
@@ -46,6 +46,8 @@
   // wpa_supplicant.
   void BSSAdded(const ::DBus::Path &BSS,
                 const std::map<std::string, ::DBus::Variant> &properties);
+  void PropertiesChanged(
+      const std::map<std::string, ::DBus::Variant> &properties);
   void ScanDone();
 
   // called by WiFiService
@@ -57,8 +59,10 @@
   virtual WiFiServiceRefPtr GetService(const KeyValueStore &args, Error *error);
 
  private:
+  friend class WiFiMainTest;  // access to supplicant_*_proxy_, link_up_
   FRIEND_TEST(WiFiMainTest, FindServiceWEP);
   FRIEND_TEST(WiFiMainTest, FindServiceWPA);
+  FRIEND_TEST(WiFiMainTest, InitialSupplicantState);  // kInterfaceStateUnknown
 
   typedef std::map<const std::string, WiFiEndpointRefPtr> EndpointMap;
   typedef std::map<const std::string, WiFiServiceRefPtr> ServiceMap;
@@ -71,6 +75,7 @@
   static const char kManagerErrorUnsupportedSecurityMode[];
   static const char kManagerErrorUnsupportedServiceType[];
   static const char kManagerErrorUnsupportedServiceMode[];
+  static const char kInterfaceStateUnknown[];
 
   WiFiServiceRefPtr FindService(const std::vector<uint8_t> &ssid,
                                 const std::string &mode,
@@ -78,6 +83,7 @@
   ByteArrays GetHiddenSSIDList();
   void ScanDoneTask();
   void ScanTask();
+  void StateChanged(const std::string &new_state);
 
   // Store cached copies of singletons for speed/ease of testing.
   ProxyFactory *proxy_factory_;
@@ -96,8 +102,9 @@
   uint16 scan_interval_;
   bool link_up_;
   std::vector<WiFiServiceRefPtr> services_;
+  WiFiServiceRefPtr pending_service_;
+  std::string supplicant_state_;
 
-  friend class WiFiMainTest;  // access to supplicant_*_proxy_, link_up_
   DISALLOW_COPY_AND_ASSIGN(WiFi);
 };
 
diff --git a/wifi_service.cc b/wifi_service.cc
index 3dd3362..6843d88 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -35,7 +35,7 @@
                          EventDispatcher *dispatcher,
                          Manager *manager,
                          const WiFiRefPtr &device,
-                         const std::vector<uint8_t> ssid,
+                         const std::vector<uint8_t> &ssid,
                          const std::string &mode,
                          const std::string &security,
                          bool hidden_ssid)
diff --git a/wifi_service.h b/wifi_service.h
index 5f7b1e9..e3ad062 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -26,7 +26,7 @@
               EventDispatcher *dispatcher,
               Manager *manager,
               const WiFiRefPtr &device,
-              const std::vector<uint8_t> ssid,
+              const std::vector<uint8_t> &ssid,
               const std::string &mode,
               const std::string &security,
               bool hidden_ssid);
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 716ebc6..5185e46 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -13,6 +13,7 @@
 #include <string>
 #include <vector>
 
+#include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/string_number_conversions.h>
 #include <base/string_util.h>
@@ -33,6 +34,7 @@
 #include "shill/mock_rtnl_handler.h"
 #include "shill/mock_supplicant_interface_proxy.h"
 #include "shill/mock_supplicant_process_proxy.h"
+#include "shill/mock_wifi_service.h"
 #include "shill/nice_mock_control.h"
 #include "shill/property_store_unittest.h"
 #include "shill/proxy_factory.h"
@@ -48,6 +50,7 @@
 using ::testing::AnyNumber;
 using ::testing::DefaultValue;
 using ::testing::InSequence;
+using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::Test;
@@ -145,6 +148,8 @@
   }
 
  protected:
+  typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;
+
   class TestProxyFactory : public ProxyFactory {
    public:
     explicit TestProxyFactory(WiFiMainTest *test) : test_(test) {}
@@ -180,13 +185,28 @@
   SupplicantInterfaceProxyInterface *GetSupplicantInterfaceProxy() {
     return wifi_->supplicant_interface_proxy_.get();
   }
-  void InitiateConnect(WiFiService *service) {
+  const string &GetSupplicantState() {
+    return wifi_->supplicant_state_;
+  }
+  void InitiateConnect(WiFiServiceRefPtr service) {
     map<string, ::DBus::Variant> params;
     wifi_->ConnectTo(service, params);
   }
   bool IsLinkUp() {
     return wifi_->link_up_;
   }
+  MockWiFiServiceRefPtr MakeMockService() {
+    vector<uint8_t> ssid(1, 'a');
+    return new MockWiFiService(
+        &control_interface_,
+        &dispatcher_,
+        &manager_,
+        wifi_,
+        ssid,
+        flimflam::kModeManaged,
+        flimflam::kSecurityNone,
+        false);
+  }
   void ReportBSS(const ::DBus::Path &bss_path,
                  const string &ssid,
                  const string &bssid,
@@ -198,6 +218,9 @@
   void ReportScanDone() {
     wifi_->ScanDoneTask();
   }
+  void ReportStateChanged(const string &new_state) {
+    wifi_->StateChanged(new_state);
+  }
   void StartWiFi() {
     wifi_->Start();
   }
@@ -739,4 +762,44 @@
   dispatcher_.DispatchPendingEvents();
 }
 
+TEST_F(WiFiMainTest, InitialSupplicantState) {
+  EXPECT_EQ(WiFi::kInterfaceStateUnknown, GetSupplicantState());
+}
+
+TEST_F(WiFiMainTest, StateChangeNoService) {
+  // State change should succeed even if there is no pending Service.
+  ReportStateChanged(wpa_supplicant::kInterfaceStateScanning);
+  EXPECT_EQ(wpa_supplicant::kInterfaceStateScanning, GetSupplicantState());
+}
+
+TEST_F(WiFiMainTest, StateChangeWithService) {
+  // Forward transition should trigger a Service state change.
+  StartWiFi();
+  dispatcher_.DispatchPendingEvents();
+  MockWiFiServiceRefPtr service = MakeMockService();
+  InitiateConnect(service);
+  EXPECT_CALL(*service.get(), SetState(Service::kStateAssociating));
+  ReportStateChanged(wpa_supplicant::kInterfaceStateAssociated);
+  // Verify expectations now, because WiFi may report other state changes
+  // when WiFi is Stop()-ed (during TearDown()).
+  Mock::VerifyAndClearExpectations(service.get());
+}
+
+TEST_F(WiFiMainTest, StateChangeBackwardsWithService) {
+  // Some backwards transitions should not trigger a Service state change.
+  // Supplicant state should still be updated, however.
+  StartWiFi();
+  dispatcher_.DispatchPendingEvents();
+  MockWiFiServiceRefPtr service = MakeMockService();
+  InitiateConnect(service);
+  ReportStateChanged(wpa_supplicant::kInterfaceStateCompleted);
+  EXPECT_CALL(*service.get(), SetState(_)).Times(0);
+  ReportStateChanged(wpa_supplicant::kInterfaceStateAuthenticating);
+  EXPECT_EQ(wpa_supplicant::kInterfaceStateAuthenticating,
+            GetSupplicantState());
+  // Verify expectations now, because WiFi may report other state changes
+  // when WiFi is Stop()-ed (during TearDown()).
+  Mock::VerifyAndClearExpectations(service.get());
+}
+
 }  // namespace shill
diff --git a/wpa_supplicant.cc b/wpa_supplicant.cc
index 2f79fac..01d5f9c 100644
--- a/wpa_supplicant.cc
+++ b/wpa_supplicant.cc
@@ -15,6 +15,16 @@
 const char kDBusPath[]              = "/fi/w1/wpa_supplicant1";
 const char kDriverNL80211[]         = "nl80211";
 const char kErrorInterfaceExists[]  = "fi.w1.wpa_supplicant1.InterfaceExists";
+const char kInterfacePropertyState[] = "State";
+const char kInterfaceState4WayHandshake[]  = "4way_handshake";
+const char kInterfaceStateAssociated[]     = "associated";
+const char kInterfaceStateAssociating[]    = "associating";
+const char kInterfaceStateAuthenticating[] = "authenticating";
+const char kInterfaceStateCompleted[]      = "completed";
+const char kInterfaceStateDisconnected[]   = "disconnected";
+const char kInterfaceStateGroupHandshake[] = "group_handshake";
+const char kInterfaceStateInactive[]       = "inactive";
+const char kInterfaceStateScanning[]       = "scanning";
 const char kKeyManagementMethodSuffixEAP[] = "-eap";
 const char kKeyManagementMethodSuffixPSK[] = "-psk";
 const char kKeyModeNone[]           = "NONE";
diff --git a/wpa_supplicant.h b/wpa_supplicant.h
index 78aeaa6..5934629 100644
--- a/wpa_supplicant.h
+++ b/wpa_supplicant.h
@@ -18,6 +18,16 @@
 extern const char kDBusPath[];
 extern const char kDriverNL80211[];
 extern const char kErrorInterfaceExists[];
+extern const char kInterfacePropertyState[];
+extern const char kInterfaceState4WayHandshake[];
+extern const char kInterfaceStateAssociated[];
+extern const char kInterfaceStateAssociating[];
+extern const char kInterfaceStateAuthenticating[];
+extern const char kInterfaceStateCompleted[];
+extern const char kInterfaceStateDisconnected[];
+extern const char kInterfaceStateGroupHandshake[];
+extern const char kInterfaceStateInactive[];
+extern const char kInterfaceStateScanning[];
 extern const char kKeyManagementMethodSuffixEAP[];
 extern const char kKeyManagementMethodSuffixPSK[];
 extern const char kKeyModeNone[];