shill: Don't let WiFi scan during association or configuration

Don't let the UI trigger scans during association or configuration to
avoid missing frames while we're off channel.  This fixes behavior where
we take a long time to connect to service (or fail completely) because
we essentially stop listening to the AP for a second or two.

BUG=chromium-os:36233
TEST=Unit tests

Change-Id: I5cd9c687209b13d6206930f55761ba355201ff82
Reviewed-on: https://gerrit.chromium.org/gerrit/38482
Commit-Ready: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
diff --git a/service.h b/service.h
index 0f80da7..2f91b39 100644
--- a/service.h
+++ b/service.h
@@ -541,6 +541,7 @@
   FRIEND_TEST(ServiceTest, SetConnectable);
   FRIEND_TEST(ServiceTest, State);
   FRIEND_TEST(ServiceTest, Unload);
+  FRIEND_TEST(WiFiMainTest, NoScansWhileConnecting);
   FRIEND_TEST(WiFiMainTest, SuspectCredentialsWPAPreviouslyConnected);
   FRIEND_TEST(WiFiTimerTest, ReconnectTimer);
 
diff --git a/wifi.cc b/wifi.cc
index febb07b..1d2689d 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -1113,6 +1113,11 @@
     SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
     return;
   }
+  if ((pending_service_.get() && pending_service_->IsConnecting()) ||
+      (current_service_.get() && current_service_->IsConnecting())) {
+    SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
+    return;
+  }
   map<string, DBus::Variant> scan_args;
   scan_args[wpa_supplicant::kPropertyScanType].writer().
       append_string(wpa_supplicant::kScanTypeActive);
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index f3a3b76..38c83db 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -311,6 +311,9 @@
   void FireScanTimer() {
     wifi_->ScanTimerHandler();
   }
+  void TriggerScan() {
+    wifi_->Scan(NULL);
+  }
   const WiFiServiceRefPtr &GetCurrentService() {
     return wifi_->current_service_;
   }
@@ -844,6 +847,38 @@
   StartWiFi();
 }
 
+TEST_F(WiFiMainTest, NoScansWhileConnecting) {
+  MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
+  StartWiFi();
+  SetPendingService(service);
+  // If we're connecting, we ignore scan requests to stay on channel.
+  service->state_ = Service::kStateConfiguring;
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+  TriggerScan();
+  dispatcher_.DispatchPendingEvents();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  // But otherwise we'll honor the request.
+  service->state_ = Service::kStateConnected;
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+  TriggerScan();
+  dispatcher_.DispatchPendingEvents();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  // Similarly, ignore scans when our connected service is reconnecting.
+  SetPendingService(NULL);
+  SetCurrentService(service);
+  service->state_ = Service::kStateAssociating;
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+  TriggerScan();
+  dispatcher_.DispatchPendingEvents();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+  // But otherwise we'll honor the request.
+  service->state_ = Service::kStateIdle;
+  EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+  TriggerScan();
+  dispatcher_.DispatchPendingEvents();
+  Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+}
+
 TEST_F(WiFiMainTest, ResumeStartsScanWhenIdle) {
   EXPECT_CALL(*supplicant_interface_proxy_, Scan(_));
   StartWiFi();
@@ -877,6 +912,7 @@
   ScopedMockLog log;
   service->AddEndpoint(ap);
   service->AutoConnect();
+  service->SetState(Service::kStateOnline);
   EXPECT_FALSE(wifi()->IsIdle());
   dispatcher_.DispatchPendingEvents();
   Mock::VerifyAndClearExpectations(&supplicant_interface_proxy_);