shill: vpn: Disconnect VPN service when underlying connection disconnects.

BUG=chromium-os:30737,chromium-os:30774
TEST=unit tests, tested on device with 000OpenVPNGenesis

Change-Id: Ib350cb23edd3e68b3bd5390378789a6b60857b80
Reviewed-on: https://gerrit.chromium.org/gerrit/22461
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/vpn_service.cc b/vpn_service.cc
index a97bddf..1d0c89e 100644
--- a/vpn_service.cc
+++ b/vpn_service.cc
@@ -12,10 +12,13 @@
 
 #include "shill/key_value_store.h"
 #include "shill/manager.h"
+#include "shill/scope_logger.h"
 #include "shill/technology.h"
 #include "shill/vpn_driver.h"
 
+using base::Bind;
 using base::StringPrintf;
+using base::Unretained;
 using std::replace_if;
 using std::string;
 
@@ -40,6 +43,7 @@
 }
 
 void VPNService::Connect(Error *error) {
+  SLOG(VPN, 2) << __func__ << " @ " << friendly_name();
   if (IsConnected() || IsConnecting()) {
     Error::PopulateAndLog(
         error, Error::kAlreadyConnected, "VPN service already connected.");
@@ -50,6 +54,7 @@
 }
 
 void VPNService::Disconnect(Error *error) {
+  SLOG(VPN, 2) << __func__ << " @ " << friendly_name();
   Service::Disconnect(error);
   driver_->Disconnect();
 }
@@ -119,4 +124,27 @@
   set_favorite(true);
 }
 
+void VPNService::SetConnection(const ConnectionRefPtr &connection) {
+  // Construct the connection binder here rather than in the constructor because
+  // this service's |friendly_name_| (which will be used for binder logging) may
+  // be initialized late. Also, there's really no reason to construct a binder
+  // if we never connect to this service. It's safe to use an unretained
+  // callback to driver's method because both the binder and the driver will be
+  // destroyed when this service is destructed.
+  if (!connection_binder_.get()) {
+    connection_binder_.reset(
+        new Connection::Binder(friendly_name(),
+                               Bind(&VPNDriver::OnConnectionDisconnected,
+                                    Unretained(driver_.get()))));
+  }
+  // Note that |connection_| is a reference-counted pointer and is always set
+  // through this method. This means that the connection binder will not be
+  // notified when the connection is destructed (because we will unbind it first
+  // here when it's set to NULL, or because the binder will already be destroyed
+  // by ~VPNService) -- it will be notified only if the connection disconnects
+  // (e.g., because an underlying connection is destructed).
+  connection_binder_->Attach(connection);
+  Service::SetConnection(connection);
+}
+
 }  // namespace shill