shill: WiFi: Reset PendingService if hidden network times out
If a hidden service times out associate, it will likely have
no endpoints, since wpa_supplicant failed to attain a CurrentBSS.
If so, the service has no reference to a wifi_ device and thus
cannot call WiFi::DisconnectFrom() to reset pending_service_.
In this case, we must perform the disconnect here ourselves.
BUG=chromium:226688
TEST=Unit tests + manual: while connected, attempt a connection
to a non-existent hidden network. After the "out-of-range"
error appears, the system should re-connect where it was
originally connected.
Change-Id: I1d3b6940f9b077b890189dea649f48405dd6a494
Reviewed-on: https://gerrit.chromium.org/gerrit/47366
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
diff --git a/wifi.cc b/wifi.cc
index 3f1021d..e769d69 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -1337,6 +1337,17 @@
CHECK(pending_service_);
pending_service_->DisconnectWithFailure(
Service::kFailureOutOfRange, &unused_error);
+
+ // A hidden service may have no endpoints, since wpa_supplicant
+ // failed to attain a CurrentBSS. If so, the service has no
+ // reference to |this| device and cannot call WiFi::DisconnectFrom()
+ // to reset pending_service_. In this case, we must perform the
+ // disconnect here ourselves.
+ if (pending_service_) {
+ CHECK(!pending_service_->HasEndpoints());
+ LOG(INFO) << "Hidden service was not found.";
+ DisconnectFrom(pending_service_);
+ }
}
void WiFi::StartReconnectTimer() {
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 49f4cc2..4223d80 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -274,6 +274,9 @@
void ThrowDBusError() {
throw DBus::Error("SomeDBusType", "A handy message");
}
+ void ResetPendingService() {
+ SetPendingService(NULL);
+ }
protected:
typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;
@@ -1241,7 +1244,7 @@
EXPECT_FALSE(GetPendingTimeout().IsCancelled());
}
-TEST_F(WiFiMainTest, TimeoutPendingService) {
+TEST_F(WiFiMainTest, TimeoutPendingServiceWithEndpoints) {
StartWiFi();
const base::CancelableClosure &pending_timeout = GetPendingTimeout();
EXPECT_TRUE(pending_timeout.IsCancelled());
@@ -1249,8 +1252,34 @@
SetupConnectingService(DBus::Path(), NULL, NULL));
EXPECT_FALSE(pending_timeout.IsCancelled());
EXPECT_EQ(service, GetPendingService());
- EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureOutOfRange, _));
+ // Simulate a service with a wifi_ reference calling DisconnectFrom().
+ EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureOutOfRange, _))
+ .WillOnce(InvokeWithoutArgs(this, &WiFiObjectTest::ResetPendingService));
+ EXPECT_CALL(*service, HasEndpoints()).Times(0);
+ // DisconnectFrom() should not be called directly from WiFi.
+ EXPECT_CALL(*service, SetState(Service::kStateIdle)).Times(0);
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect()).Times(0);
pending_timeout.callback().Run();
+ Mock::VerifyAndClearExpectations(service);
+}
+
+TEST_F(WiFiMainTest, TimeoutPendingServiceWithoutEndpoints) {
+ StartWiFi();
+ const base::CancelableClosure &pending_timeout = GetPendingTimeout();
+ EXPECT_TRUE(pending_timeout.IsCancelled());
+ MockWiFiServiceRefPtr service(
+ SetupConnectingService(DBus::Path(), NULL, NULL));
+ EXPECT_FALSE(pending_timeout.IsCancelled());
+ EXPECT_EQ(service, GetPendingService());
+ // We expect the service to get a disconnect call, but in this scenario
+ // the service does nothing.
+ EXPECT_CALL(*service, DisconnectWithFailure(Service::kFailureOutOfRange, _));
+ EXPECT_CALL(*service, HasEndpoints()).WillOnce(Return(false));
+ // DisconnectFrom() should be called directly from WiFi.
+ EXPECT_CALL(*service, SetState(Service::kStateIdle)).Times(AtLeast(1));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Disconnect());
+ pending_timeout.callback().Run();
+ EXPECT_EQ(NULL, GetPendingService().get());
}
TEST_F(WiFiMainTest, DisconnectInvalidService) {