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();