shill: Manager: Create and emit signals for the ConnectedState property

As opposed to the "State" property of the Manager which is strictly
"offline" or "online", this new property returns the connected state
of the highest-ranked service.  This allows observers to tell whether
or not to expect to have wider network connectivity by waiting for
this property to be "online", with the assurance that portal detection
has completed successfully.

CQ-DEPEND=CL:170701
BUG=chromium:298568
TEST=New unit test + manual: run "dbus-monitor --system" and restart
shill.  Expect to see updates to the "ConnectionState" of the manager;
initially "idle" as the manager starts:

  signal sender=:1.20 -> dest=(null destination) serial=11 path=/; interface=org.chromium.flimflam.Manager; member=PropertyChanged
   string "ConnectionState"
   variant       string "idle"

then ultimately if the connection succeeds:

signal sender=:1.20 -> dest=(null destination) serial=62 path=/; interface=org.c
hromium.flimflam.Manager; member=PropertyChanged
   string "ConnectionState"
   variant       string "online"

Change-Id: I2c34e33c6742cbe7516af7208131e76344b2f690
Reviewed-on: https://chromium-review.googlesource.com/170704
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 1dc9849..1d36318 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -269,6 +269,10 @@
     EXPECT_TRUE(manager()->sort_services_task_.IsCancelled());
   }
 
+  void RefreshConnectionState() {
+    manager()->RefreshConnectionState();
+  }
+
   RpcIdentifier GetDefaultServiceRpcIdentifier() {
     return manager()->GetDefaultServiceRpcIdentifier(NULL);
   }
@@ -3223,6 +3227,41 @@
   manager()->DeregisterService(mock_service1);
 }
 
+TEST_F(ManagerTest, RefreshConnectionState) {
+  EXPECT_CALL(*manager_adaptor_,
+              EmitStringChanged(kConnectionStateProperty, kStateIdle));
+  RefreshConnectionState();
+  Mock::VerifyAndClearExpectations(manager_adaptor_);
+
+  scoped_refptr<MockService> mock_service(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                metrics(),
+                                manager()));
+  EXPECT_CALL(*manager_adaptor_,
+              EmitStringChanged(kConnectionStateProperty, _)).Times(0);
+  manager()->RegisterService(mock_service);
+  RefreshConnectionState();
+
+  scoped_refptr<MockConnection> mock_connection(
+      new NiceMock<MockConnection>(device_info_.get()));
+  mock_service->set_mock_connection(mock_connection);
+  EXPECT_CALL(*mock_service, state())
+      .WillOnce(Return(Service::kStateIdle));
+  RefreshConnectionState();
+
+  Mock::VerifyAndClearExpectations(manager_adaptor_);
+  EXPECT_CALL(*mock_service, state())
+      .WillOnce(Return(Service::kStatePortal));
+  EXPECT_CALL(*manager_adaptor_,
+              EmitStringChanged(kConnectionStateProperty, kStatePortal));
+  RefreshConnectionState();
+  Mock::VerifyAndClearExpectations(manager_adaptor_);
+
+  mock_service->set_mock_connection(NULL);
+  manager()->DeregisterService(mock_service);
+}
+
 TEST_F(ManagerTest, StartupPortalList) {
   // Simulate loading value from the default profile.
   const string kProfileValue("wifi,vpn");