Pending service connection attempt.

Disconnect the pending service when wpa_supplicant is connected to
a service that's neither pending or current. We get into this state
when we have two connection attempts to two different services in a
quick succession, where the second attempt failed because of the
cfg80211 driver is still busy with the first connection attempt. Thus,
the connection attempt to the second service is ignored by wpa_supplicant.

By disconnecting the pending service in this situation, it will send a
"Disconnect" command to wpa_supplicant, which will disconnect the unintended
service. It will also mark the pending service as connectable (not connecting),
and kick off the connection attempt by manager for the pending service sooner
rather than waiting for the pending timeout.

BUG=chrome-os-partner:29436
TEST=unit tests, run network_WiFi_ProfileBasic

Change-Id: I7faeeb42a324548e5b2df892781dd6e668cdf0fe
Reviewed-on: https://chromium-review.googlesource.com/203147
Tested-by: Peter Qiu <zqiu@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Queue: Peter Qiu <zqiu@chromium.org>
diff --git a/wifi.cc b/wifi.cc
index a1d4f8c..0069f7b 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -976,9 +976,12 @@
                    << (current_service_ ?
                        current_service_->unique_name() :
                        "(NULL)");
-      // Although we didn't expect to get here, we should keep
-      // |current_service_| in sync with what supplicant has done.
-      current_service_ = service;
+      // wpa_supplicant has no knowledge of the pending_service_ at this point.
+      // Disconnect the pending_service_, so that it can be connectable again.
+      // Otherwise, we'd have to wait for the pending timeout to trigger the
+      // disconnect. This will speed up the connection attempt process for
+      // the pending_service_.
+      DisconnectFrom(pending_service_);
     }
     return;
   }
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 271c1ae..d6b5386 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -2239,6 +2239,28 @@
   EXPECT_EQ(NULL, GetCurrentService().get());
 }
 
+TEST_F(WiFiMainTest, ConnectedToUnintendedPreemptsPending) {
+  StartWiFi();
+  ::DBus::Path bss_path;
+  // Connecting two different services back-to-back.
+  MockWiFiServiceRefPtr unintended_service(
+      SetupConnectingService(DBus::Path(), NULL, &bss_path));
+  MockWiFiServiceRefPtr intended_service(
+      SetupConnectingService(DBus::Path(), NULL, NULL));
+
+  // Verify the pending service.
+  EXPECT_EQ(intended_service.get(), GetPendingService().get());
+
+  // Connected to the unintended service (service0).
+  ReportCurrentBSSChanged(bss_path);
+
+  // Verify the pending service is disconnected, and the service state is back
+  // to idle, so it is connectable again.
+  EXPECT_EQ(NULL, GetPendingService().get());
+  EXPECT_EQ(NULL, GetCurrentService().get());
+  EXPECT_EQ(Service::kStateIdle, intended_service->state());
+}
+
 TEST_F(WiFiMainTest, IsIdle) {
   StartWiFi();
   EXPECT_TRUE(wifi()->IsIdle());