Add Service::OnDefaultServiceStateChanged() callback
This allows VPNs to figure out when the default physical service has
transitioned back to the Online state, so they can trigger a
reconnection.
Bug: None
BUG=chromium:598781
TEST=`FEATURES=test emerge-link shill`
Change-Id: I331eb12cbcd5f60c4fa1687d55fcca58331316fe
diff --git a/manager.cc b/manager.cc
index afa07e9..39cc879 100644
--- a/manager.cc
+++ b/manager.cc
@@ -1466,6 +1466,16 @@
SortServices();
}
+void Manager::NotifyServiceStateChanged(const ServiceRefPtr& to_update) {
+ UpdateService(to_update);
+ if (to_update != last_default_physical_service_) {
+ return;
+ }
+ for (const auto& service : services_) {
+ service->OnDefaultServiceStateChanged(to_update);
+ }
+}
+
void Manager::UpdateDevice(const DeviceRefPtr& to_update) {
LOG(INFO) << "Device " << to_update->link_name() << " updated: "
<< (to_update->enabled_persistent() ? "enabled" : "disabled");
diff --git a/manager.h b/manager.h
index 772d6a1..6a1fca5 100644
--- a/manager.h
+++ b/manager.h
@@ -159,6 +159,9 @@
// for disconnecting the Service before-hand.
virtual void DeregisterService(const ServiceRefPtr& to_forget);
virtual void UpdateService(const ServiceRefPtr& to_update);
+ // Called when any service's state changes. Informs other services
+ // (e.g. VPNs) if the default physical service's state has changed.
+ virtual void NotifyServiceStateChanged(const ServiceRefPtr& to_update);
// Persists |to_update| into an appropriate profile.
virtual void UpdateDevice(const DeviceRefPtr& to_update);
@@ -590,6 +593,7 @@
FRIEND_TEST(ManagerTest, ConnectToBestServices);
FRIEND_TEST(ManagerTest, CreateConnectivityReport);
FRIEND_TEST(ManagerTest, DefaultTechnology);
+ FRIEND_TEST(ManagerTest, DefaultServiceStateChange);
FRIEND_TEST(ManagerTest, DetectMultiHomedDevices);
FRIEND_TEST(ManagerTest, DeviceClaimerVanishedTask);
FRIEND_TEST(ManagerTest, DevicePresenceStatusCheck);
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 547bc6b..7b1bd6a 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -2760,6 +2760,42 @@
EXPECT_TRUE(manager()->default_service_callbacks_.empty());
}
+TEST_F(ManagerTest, DefaultServiceStateChange) {
+ MockMetrics mock_metrics(dispatcher());
+ SetMetrics(&mock_metrics);
+
+ scoped_refptr<MockService> mock_service0(
+ new NiceMock<MockService>(
+ control_interface(), dispatcher(), metrics(), manager()));
+ scoped_refptr<MockService> mock_service1(
+ new NiceMock<MockService>(
+ control_interface(), dispatcher(), metrics(), manager()));
+
+ manager()->RegisterService(mock_service0);
+ manager()->RegisterService(mock_service1);
+
+ EXPECT_CALL(mock_metrics, NotifyDefaultServiceChanged(mock_service0.get()));
+ manager()->UpdateDefaultServices(mock_service0, mock_service0);
+
+ // Changing the default service's state should notify both services.
+ EXPECT_CALL(*mock_service0.get(), OnDefaultServiceStateChanged(_));
+ EXPECT_CALL(*mock_service1.get(), OnDefaultServiceStateChanged(_));
+ manager()->NotifyServiceStateChanged(mock_service0);
+ Mock::VerifyAndClearExpectations(mock_service0.get());
+ Mock::VerifyAndClearExpectations(mock_service1.get());
+
+ // Changing the non-default service's state shouldn't notify anyone.
+ EXPECT_CALL(*mock_service0.get(), OnDefaultServiceStateChanged(_)).Times(0);
+ EXPECT_CALL(*mock_service1.get(), OnDefaultServiceStateChanged(_)).Times(0);
+ manager()->NotifyServiceStateChanged(mock_service1);
+
+ EXPECT_CALL(mock_metrics, NotifyDefaultServiceChanged(nullptr));
+ manager()->UpdateDefaultServices(nullptr, nullptr);
+
+ manager()->DeregisterService(mock_service1);
+ manager()->DeregisterService(mock_service0);
+}
+
TEST_F(ManagerTest, ReportServicesOnSameNetwork) {
int connection_id1 = 100;
int connection_id2 = 200;
diff --git a/mock_service.h b/mock_service.h
index 1ae54b3..cdc9647 100644
--- a/mock_service.h
+++ b/mock_service.h
@@ -90,6 +90,8 @@
MOCK_METHOD0(EnableAndRetainAutoConnect, void());
MOCK_METHOD1(OnBeforeSuspend, void(const ResultCallback& callback));
MOCK_METHOD0(OnAfterResume, void());
+ MOCK_METHOD1(OnDefaultServiceStateChanged,
+ void(const ServiceRefPtr& service));
// Set a string for this Service via |store|. Can be wired to Save() for
// test purposes.
diff --git a/service.cc b/service.cc
index 522b54b..256ec55 100644
--- a/service.cc
+++ b/service.cc
@@ -440,7 +440,7 @@
reenable_auto_connect_task_.Cancel();
}
UpdateErrorProperty();
- manager_->UpdateService(this);
+ manager_->NotifyServiceStateChanged(this);
metrics_->NotifyServiceStateChanged(*this, state);
adaptor_->EmitStringChanged(kStateProperty, GetStateString());
}
@@ -1232,6 +1232,10 @@
// Nothing to do in the general case.
}
+void Service::OnDefaultServiceStateChanged(const ServiceRefPtr& parent) {
+ // Nothing to do in the general case.
+}
+
string Service::GetIPConfigRpcIdentifier(Error* error) const {
if (!connection_) {
error->Populate(Error::kNotFound);
diff --git a/service.h b/service.h
index 125e31f..e35d17d 100644
--- a/service.h
+++ b/service.h
@@ -502,6 +502,10 @@
// Called by the manager once when entering dark resume.
virtual void OnDarkResume();
+ // Called by the manager when the default physical service's state has
+ // changed.
+ virtual void OnDefaultServiceStateChanged(const ServiceRefPtr& parent);
+
// Called by the manager to clear remembered state of being explicitly
// disconnected.
virtual void ClearExplicitlyDisconnected();