Implement {Available|Connected|Enabled}Technolgies and DefaultTechnology

This is Part 1 of a series of changes required for the Manager to generate
the right set of DBus events allowing shill to properly tell Chrome about
connectivity.

Some other side-effects in the change (needed to implement the above):
- Emit these manager properties over DBus everytime we re-sort the Service
  vector.
- Add a technology data member to Device, this allows easy enumeration of
  technologies.
- Add interface for getting a handle to the selected service on a device.
- Map connected state to online instead of ready state. This is needed for
  so that Chrome doesn't wait for the portal detection code to switch the
  state from ready->online.
- Default set the ethernet service as connectable.

BUG=chromium-os:14887
TEST=1) manual test to verify shill properly configures the ethernet
     device and Chrome correctly detects that.
     2) new unit tests

Change-Id: Ib10f2f0836f8d4aacaad1491f58ed9b3b5d37e7d
Reviewed-on: https://gerrit.chromium.org/gerrit/12019
Tested-by: Gaurav Shah <gauravsh@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
diff --git a/adaptor_interfaces.h b/adaptor_interfaces.h
index 10e4f2c..640053f 100644
--- a/adaptor_interfaces.h
+++ b/adaptor_interfaces.h
@@ -47,11 +47,11 @@
   // RPC interface to which the implementation is adapting.
   virtual const std::string &GetRpcIdentifier() = 0;
 
-  virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
-  virtual void EmitUintChanged(const std::string& name, uint32 value) = 0;
-  virtual void EmitIntChanged(const std::string& name, int value) = 0;
-  virtual void EmitStringChanged(const std::string& name,
-                                 const std::string& value) = 0;
+  virtual void EmitBoolChanged(const std::string &name, bool value) = 0;
+  virtual void EmitUintChanged(const std::string &name, uint32 value) = 0;
+  virtual void EmitIntChanged(const std::string &name, int value) = 0;
+  virtual void EmitStringChanged(const std::string &name,
+                                 const std::string &value) = 0;
 };
 
 // These are the functions that a Manager adaptor must support
@@ -65,16 +65,18 @@
 
   virtual void UpdateRunning() = 0;
 
-  virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
-  virtual void EmitUintChanged(const std::string& name, uint32 value) = 0;
-  virtual void EmitIntChanged(const std::string& name, int value) = 0;
-  virtual void EmitStringChanged(const std::string& name,
-                                 const std::string& value) = 0;
+  virtual void EmitBoolChanged(const std::string &name, bool value) = 0;
+  virtual void EmitUintChanged(const std::string &name, uint32 value) = 0;
+  virtual void EmitIntChanged(const std::string &name, int value) = 0;
+  virtual void EmitStringChanged(const std::string &name,
+                                 const std::string &value) = 0;
+  virtual void EmitStringsChanged(const std::string &name,
+                                  const std::vector<std::string> &value) = 0;
   virtual void EmitRpcIdentifierArrayChanged(
       const std::string &name,
       const std::vector<std::string> &value) = 0;
 
-  virtual void EmitStateChanged(const std::string& new_state) = 0;
+  virtual void EmitStateChanged(const std::string &new_state) = 0;
 };
 
 // These are the functions that a Profile adaptor must support
@@ -86,11 +88,11 @@
   // RPC interface to which the implementation is adapting.
   virtual const std::string &GetRpcIdentifier() = 0;
 
-  virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
-  virtual void EmitUintChanged(const std::string& name, uint32 value) = 0;
-  virtual void EmitIntChanged(const std::string& name, int value) = 0;
-  virtual void EmitStringChanged(const std::string& name,
-                                 const std::string& value) = 0;
+  virtual void EmitBoolChanged(const std::string &name, bool value) = 0;
+  virtual void EmitUintChanged(const std::string &name, uint32 value) = 0;
+  virtual void EmitIntChanged(const std::string &name, int value) = 0;
+  virtual void EmitStringChanged(const std::string &name,
+                                 const std::string &value) = 0;
 };
 
 // These are the functions that a Service adaptor must support
@@ -104,11 +106,11 @@
 
   virtual void UpdateConnected() = 0;
 
-  virtual void EmitBoolChanged(const std::string& name, bool value) = 0;
-  virtual void EmitUintChanged(const std::string& name, uint32 value) = 0;
-  virtual void EmitIntChanged(const std::string& name, int value) = 0;
-  virtual void EmitStringChanged(const std::string& name,
-                                 const std::string& value) = 0;
+  virtual void EmitBoolChanged(const std::string &name, bool value) = 0;
+  virtual void EmitUintChanged(const std::string &name, uint32 value) = 0;
+  virtual void EmitIntChanged(const std::string &name, int value) = 0;
+  virtual void EmitStringChanged(const std::string &name,
+                                 const std::string &value) = 0;
 };
 
 }  // namespace shill
diff --git a/cellular.cc b/cellular.cc
index d8cf370..9b43507 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -31,6 +31,7 @@
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
 #include "shill/rtnl_handler.h"
+#include "shill/technology.h"
 
 using std::map;
 using std::string;
@@ -96,7 +97,8 @@
              manager,
              link_name,
              address,
-             interface_index),
+             interface_index,
+             Technology::kCellular),
       proxy_factory_(ProxyFactory::GetInstance()),
       state_(kStateDisabled),
       modem_state_(kModemStateUnknown),
diff --git a/cellular_service.cc b/cellular_service.cc
index d27a360..2daf880 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -24,7 +24,7 @@
                                  EventDispatcher *dispatcher,
                                  Manager *manager,
                                  const CellularRefPtr &device)
-    : Service(control_interface, dispatcher, manager, flimflam::kTypeCellular),
+    : Service(control_interface, dispatcher, manager, Technology::kCellular),
       strength_(0),
       cellular_(device) {
   PropertyStore *store = this->mutable_store();
diff --git a/device.cc b/device.cc
index 81adb00..ad9e766 100644
--- a/device.cc
+++ b/device.cc
@@ -30,6 +30,7 @@
 #include "shill/rtnl_handler.h"
 #include "shill/service.h"
 #include "shill/store_interface.h"
+#include "shill/technology.h"
 
 using base::StringPrintf;
 using std::string;
@@ -48,7 +49,8 @@
                Manager *manager,
                const string &link_name,
                const string &address,
-               int interface_index)
+               int interface_index,
+               Technology::Identifier technology)
     : powered_(true),
       reconnect_(true),
       hardware_address_(address),
@@ -60,6 +62,7 @@
       dispatcher_(dispatcher),
       manager_(manager),
       adaptor_(control_interface->CreateDeviceAdaptor(this)),
+      technology_(technology),
       dhcp_provider_(DHCPProvider::GetInstance()),
       rtnl_handler_(RTNLHandler::GetInstance()) {
   store_.RegisterConstString(flimflam::kAddressProperty, &hardware_address_);
@@ -180,6 +183,12 @@
                         "Device doesn't support ChangePIN.");
 }
 
+bool Device::IsConnected() const {
+  if (selected_service_)
+    return selected_service_->IsConnected();
+  return false;
+}
+
 string Device::GetRpcIdentifier() {
   return adaptor_->GetRpcIdentifier();
 }
diff --git a/device.h b/device.h
index bc8dd9d..299b0c6 100644
--- a/device.h
+++ b/device.h
@@ -41,7 +41,8 @@
          Manager *manager,
          const std::string &link_name,
          const std::string &address,
-         int interface_index);
+         int interface_index,
+         Technology::Identifier technology);
   virtual ~Device();
 
   virtual void Start();
@@ -51,6 +52,9 @@
   // to Start()), or destroyed (if its refcount falls to zero).
   virtual void Stop();
 
+
+  // TODO(gauravsh): We do not really need this since technology() can be used
+  //                 to get a device's technology for direct comparison.
   // Base method always returns false.
   virtual bool TechnologyIs(const Technology::Identifier type) const;
 
@@ -70,6 +74,11 @@
                          const std::string &new_pin,
                          Error *error);
 
+  // Returns true if the selected service on the device (if any) is connected.
+  // Returns false if there is no selected service, or if the selected service
+  // is not connected.
+  bool IsConnected() const;
+
   std::string GetRpcIdentifier();
   std::string GetStorageIdentifier();
 
@@ -78,6 +87,7 @@
   int interface_index() const { return interface_index_; }
   const ConnectionRefPtr &connection() const { return connection_; }
   bool powered() const { return powered_; }
+  virtual Technology::Identifier technology() const { return technology_; }
 
   const std::string &FriendlyName() const;
 
@@ -112,6 +122,8 @@
   FRIEND_TEST(DeviceTest, SelectedService);
   FRIEND_TEST(DeviceTest, Stop);
   FRIEND_TEST(ManagerTest, DeviceRegistrationAndStart);
+  FRIEND_TEST(ManagerTest, ConnectedTechnologies);
+  FRIEND_TEST(ManagerTest, DefaultTechnology);
   FRIEND_TEST(WiFiMainTest, Connect);
 
   // If there's an IP configuration in |ipconfig_|, releases the IP address and
@@ -188,6 +200,7 @@
   IPConfigRefPtr ipconfig_;
   ConnectionRefPtr connection_;
   scoped_ptr<DeviceAdaptorInterface> adaptor_;
+  Technology::Identifier technology_;
 
   // Maintain a reference to the connected / connecting service
   ServiceRefPtr selected_service_;
diff --git a/device_stub.h b/device_stub.h
index 840eb8c..bc866a1 100644
--- a/device_stub.h
+++ b/device_stub.h
@@ -34,16 +34,14 @@
              int interface_index,
              Technology::Identifier technology)
       : Device(control_interface, dispatcher, manager, link_name, address,
-               interface_index),
-        technology_(technology) {}
+               interface_index, technology) {}
   void Start() {}
   void Stop() {}
   bool TechnologyIs(const Technology::Identifier type) const {
-    return type == technology_;
+    return type == technology();
   }
 
  private:
-  Technology::Identifier technology_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceStub);
 };
diff --git a/device_unittest.cc b/device_unittest.cc
index 89493d6..20ca565 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -30,6 +30,7 @@
 #include "shill/mock_service.h"
 #include "shill/mock_store.h"
 #include "shill/property_store_unittest.h"
+#include "shill/technology.h"
 
 using std::map;
 using std::string;
@@ -52,7 +53,8 @@
                            NULL,
                            kDeviceName,
                            kDeviceAddress,
-                           0)) {
+                           0,
+                           Technology::kUnknown)) {
     DHCPProvider::GetInstance()->glib_ = glib();
     DHCPProvider::GetInstance()->control_interface_ = control_interface();
   }
diff --git a/ethernet.cc b/ethernet.cc
index f8e2b55..8fe8caf 100644
--- a/ethernet.cc
+++ b/ethernet.cc
@@ -37,7 +37,8 @@
              manager,
              link_name,
              address,
-             interface_index),
+             interface_index,
+             Technology::kEthernet),
       service_registered_(false),
       link_up_(false) {
   VLOG(2) << "Ethernet device " << link_name << " initialized.";
diff --git a/ethernet_service.cc b/ethernet_service.cc
index a450460..07459b7 100644
--- a/ethernet_service.cc
+++ b/ethernet_service.cc
@@ -34,8 +34,9 @@
                                  EventDispatcher *dispatcher,
                                  Manager *manager,
                                  const EthernetRefPtr &device)
-    : Service(control_interface, dispatcher, manager, flimflam::kTypeEthernet),
+    : Service(control_interface, dispatcher, manager, Technology::kEthernet),
       ethernet_(device) {
+  set_connectable(true);
   set_auto_connect(true);
 }
 
diff --git a/manager.cc b/manager.cc
index c9dda91..d7824ac 100644
--- a/manager.cc
+++ b/manager.cc
@@ -15,6 +15,7 @@
 #include <base/file_util.h>
 #include <base/logging.h>
 #include <base/memory/ref_counted.h>
+#include <base/stl_util-inl.h>
 #include <base/string_split.h>
 #include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
@@ -38,6 +39,7 @@
 #include "shill/wifi_service.h"
 
 using std::map;
+using std::set;
 using std::string;
 using std::vector;
 
@@ -318,6 +320,12 @@
   // unit tests sometimes do things in otherwise invalid states.
   if (running_ && to_manage->powered())
     to_manage->Start();
+
+  Error error;
+  adaptor_->EmitStringsChanged(flimflam::kAvailableTechnologiesProperty,
+                               AvailableTechnologies(&error));
+  adaptor_->EmitStringsChanged(flimflam::kEnabledTechnologiesProperty,
+                               EnabledTechnologies(&error));
 }
 
 void Manager::DeregisterDevice(const DeviceRefPtr &to_forget) {
@@ -327,6 +335,11 @@
       VLOG(2) << "Deregistered device: " << to_forget->UniqueName();
       to_forget->Stop();
       devices_.erase(it);
+      Error error;
+      adaptor_->EmitStringsChanged(flimflam::kAvailableTechnologiesProperty,
+                                   AvailableTechnologies(&error));
+      adaptor_->EmitStringsChanged(flimflam::kEnabledTechnologiesProperty,
+                                   EnabledTechnologies(&error));
       return;
     }
   }
@@ -429,6 +442,12 @@
   }
   adaptor_->EmitRpcIdentifierArrayChanged(flimflam::kServicesProperty,
                                           service_paths);
+
+  Error error;
+  adaptor_->EmitStringsChanged(flimflam::kConnectedTechnologiesProperty,
+                               ConnectedTechnologies(&error));
+  adaptor_->EmitStringChanged(flimflam::kDefaultTechnologyProperty,
+                              DefaultTechnology(&error));
 }
 
 string Manager::CalculateState(Error */*error*/) {
@@ -436,19 +455,35 @@
 }
 
 vector<string> Manager::AvailableTechnologies(Error */*error*/) {
-  return vector<string>();
+  set<string> unique_technologies;
+  for (vector<DeviceRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    unique_technologies.insert(
+        Technology::NameFromIdentifier((*it)->technology()));
+  }
+  return vector<string>(unique_technologies.begin(), unique_technologies.end());
 }
 
 vector<string> Manager::ConnectedTechnologies(Error */*error*/) {
-  return vector<string>();
+  set<string> unique_technologies;
+  for (vector<DeviceRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    if ((*it)->IsConnected())
+      unique_technologies.insert(
+          Technology::NameFromIdentifier((*it)->technology()));
+  }
+  return vector<string>(unique_technologies.begin(), unique_technologies.end());
 }
 
-string Manager::DefaultTechnology(Error */*error*/) {
-  return "";
+string Manager::DefaultTechnology(Error *error) {
+  return (!services_.empty() && services_[0]->IsConnected()) ?
+      services_[0]->GetTechnologyString(error) : "";
 }
 
-vector<string> Manager::EnabledTechnologies(Error */*error*/) {
-  return vector<string>();
+vector<string> Manager::EnabledTechnologies(Error *error) {
+  // TODO(gauravsh): This must be wired up to the RPC interface to handle
+  // enabled/disabled devices as set by the user. crosbug.com/23319
+  return AvailableTechnologies(error);
 }
 
 vector<string> Manager::EnumerateDevices(Error */*error*/) {
diff --git a/manager.h b/manager.h
index 90c66af..b57cea4 100644
--- a/manager.h
+++ b/manager.h
@@ -99,6 +99,9 @@
   FRIEND_TEST(ManagerTest, DeviceRegistrationAndStart);
   FRIEND_TEST(ManagerTest, PushPopProfile);
   FRIEND_TEST(ManagerTest, SortServices);
+  FRIEND_TEST(ManagerTest, AvailableTechnologies);
+  FRIEND_TEST(ManagerTest, ConnectedTechnologies);
+  FRIEND_TEST(ManagerTest, DefaultTechnology);
 
   static const char kManagerErrorNoDevice[];
 
diff --git a/manager_dbus_adaptor.cc b/manager_dbus_adaptor.cc
index 27bba64..0bf611c 100644
--- a/manager_dbus_adaptor.cc
+++ b/manager_dbus_adaptor.cc
@@ -57,6 +57,11 @@
   PropertyChanged(name, DBusAdaptor::StringToVariant(value));
 }
 
+void ManagerDBusAdaptor::EmitStringsChanged(const string &name,
+                                            const vector<string> &value) {
+  PropertyChanged(name, DBusAdaptor::StringsToVariant(value));
+}
+
 void ManagerDBusAdaptor::EmitRpcIdentifierArrayChanged(
     const string &name,
     const vector<string> &value) {
diff --git a/manager_dbus_adaptor.h b/manager_dbus_adaptor.h
index bc72273..d67128e 100644
--- a/manager_dbus_adaptor.h
+++ b/manager_dbus_adaptor.h
@@ -41,6 +41,8 @@
   void EmitUintChanged(const std::string &name, uint32 value);
   void EmitIntChanged(const std::string &name, int value);
   void EmitStringChanged(const std::string &name, const std::string &value);
+  void EmitStringsChanged(const std::string &name,
+                          const std::vector<std::string> &value);
   void EmitRpcIdentifierArrayChanged(
       const std::string &name, const std::vector<std::string> &value);
   void EmitStateChanged(const std::string &new_state);
diff --git a/manager_unittest.cc b/manager_unittest.cc
index bf440cb..8a4d223 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -42,9 +42,11 @@
 namespace shill {
 using ::testing::_;
 using ::testing::AnyNumber;
+using ::testing::ContainerEq;
 using ::testing::Ne;
 using ::testing::NiceMock;
 using ::testing::Return;
+using ::testing::StrEq;
 using ::testing::Test;
 
 class ManagerTest : public PropertyStoreTest {
@@ -74,6 +76,12 @@
                                                      "null2",
                                                      "addr2",
                                                      2));
+    mock_devices_.push_back(new NiceMock<MockDevice>(control_interface(),
+                                                     dispatcher(),
+                                                     manager(),
+                                                     "null3",
+                                                     "addr3",
+                                                     3));
     manager()->connect_profiles_to_rpc_ = false;
   }
   virtual ~ManagerTest() {}
@@ -200,18 +208,18 @@
   ON_CALL(*mock_devices_[1].get(), TechnologyIs(Technology::kWifi))
       .WillByDefault(Return(true));
 
-  manager()->RegisterDevice(mock_devices_[0].get());
-  manager()->RegisterDevice(mock_devices_[1].get());
+  manager()->RegisterDevice(mock_devices_[0]);
+  manager()->RegisterDevice(mock_devices_[1]);
 
   ASSERT_TRUE(IsDeviceRegistered(mock_devices_[0], Technology::kEthernet));
   ASSERT_TRUE(IsDeviceRegistered(mock_devices_[1], Technology::kWifi));
 
   EXPECT_CALL(*mock_devices_[0].get(), Stop());
-  manager()->DeregisterDevice(mock_devices_[0].get());
+  manager()->DeregisterDevice(mock_devices_[0]);
   EXPECT_FALSE(IsDeviceRegistered(mock_devices_[0], Technology::kEthernet));
 
   EXPECT_CALL(*mock_devices_[1].get(), Stop());
-  manager()->DeregisterDevice(mock_devices_[1].get());
+  manager()->DeregisterDevice(mock_devices_[1]);
   EXPECT_FALSE(IsDeviceRegistered(mock_devices_[1], Technology::kWifi));
 }
 
@@ -354,8 +362,8 @@
 TEST_F(ManagerTest, GetDevicesProperty) {
   ProfileRefPtr profile(new MockProfile(control_interface(), manager(), ""));
   AdoptProfile(manager(), profile);
-  manager()->RegisterDevice(mock_devices_[0].get());
-  manager()->RegisterDevice(mock_devices_[1].get());
+  manager()->RegisterDevice(mock_devices_[0]);
+  manager()->RegisterDevice(mock_devices_[1]);
   {
     map<string, ::DBus::Variant> props;
     ::DBus::Error dbus_error;
@@ -702,7 +710,7 @@
   EXPECT_TRUE(error.IsSuccess());
   EXPECT_TRUE(ServiceOrderIs(mock_service0, mock_service1));
 
-  // Priority
+  // Priority.
   mock_service0->set_priority(1);
   manager()->UpdateService(mock_service0);
   EXPECT_TRUE(ServiceOrderIs(mock_service0, mock_service1));
@@ -712,15 +720,19 @@
   manager()->UpdateService(mock_service1);
   EXPECT_TRUE(ServiceOrderIs(mock_service1, mock_service0));
 
-  // Connecting
+  // Connecting.
   EXPECT_CALL(*mock_service0.get(), state())
       .WillRepeatedly(Return(Service::kStateAssociating));
+  EXPECT_CALL(*mock_service0.get(), IsConnecting())
+      .WillRepeatedly(Return(true));
   manager()->UpdateService(mock_service0);
   EXPECT_TRUE(ServiceOrderIs(mock_service0, mock_service1));
 
-  // Connected
+  // Connected.
   EXPECT_CALL(*mock_service1.get(), state())
       .WillRepeatedly(Return(Service::kStateConnected));
+  EXPECT_CALL(*mock_service1.get(), IsConnected())
+      .WillRepeatedly(Return(true));
   manager()->UpdateService(mock_service1);
   EXPECT_TRUE(ServiceOrderIs(mock_service1, mock_service0));
 
@@ -728,6 +740,134 @@
   manager()->DeregisterService(mock_service1);
 }
 
+TEST_F(ManagerTest, AvailableTechnologies) {
+  mock_devices_.push_back(new NiceMock<MockDevice>(control_interface(),
+                                                   dispatcher(),
+                                                   manager(),
+                                                   "null4",
+                                                   "addr4",
+                                                   0));
+  manager()->RegisterDevice(mock_devices_[0]);
+  manager()->RegisterDevice(mock_devices_[1]);
+  manager()->RegisterDevice(mock_devices_[2]);
+  manager()->RegisterDevice(mock_devices_[3]);
+
+  ON_CALL(*mock_devices_[0].get(), technology())
+      .WillByDefault(Return(Technology::kEthernet));
+  ON_CALL(*mock_devices_[1].get(), technology())
+      .WillByDefault(Return(Technology::kWifi));
+  ON_CALL(*mock_devices_[2].get(), technology())
+      .WillByDefault(Return(Technology::kCellular));
+  ON_CALL(*mock_devices_[3].get(), technology())
+      .WillByDefault(Return(Technology::kWifi));
+
+  set<string> expected_technologies;
+  expected_technologies.insert(Technology::NameFromIdentifier(
+      Technology::kEthernet));
+  expected_technologies.insert(Technology::NameFromIdentifier(
+      Technology::kWifi));
+  expected_technologies.insert(Technology::NameFromIdentifier(
+      Technology::kCellular));
+  Error error;
+  vector<string> technologies = manager()->AvailableTechnologies(&error);
+
+  EXPECT_THAT(set<string>(technologies.begin(), technologies.end()),
+              ContainerEq(expected_technologies));
+}
+
+TEST_F(ManagerTest, ConnectedTechnologies) {
+  scoped_refptr<MockService> connected_service1(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+  scoped_refptr<MockService> connected_service2(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+  scoped_refptr<MockService> disconnected_service1(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+  scoped_refptr<MockService> disconnected_service2(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+
+  ON_CALL(*connected_service1.get(), IsConnected())
+      .WillByDefault(Return(true));
+  ON_CALL(*connected_service2.get(), IsConnected())
+      .WillByDefault(Return(true));
+
+  manager()->RegisterService(connected_service1);
+  manager()->RegisterService(connected_service2);
+  manager()->RegisterService(disconnected_service1);
+  manager()->RegisterService(disconnected_service2);
+
+  manager()->RegisterDevice(mock_devices_[0]);
+  manager()->RegisterDevice(mock_devices_[1]);
+  manager()->RegisterDevice(mock_devices_[2]);
+  manager()->RegisterDevice(mock_devices_[3]);
+
+  ON_CALL(*mock_devices_[0].get(), technology())
+      .WillByDefault(Return(Technology::kEthernet));
+  ON_CALL(*mock_devices_[1].get(), technology())
+      .WillByDefault(Return(Technology::kWifi));
+  ON_CALL(*mock_devices_[2].get(), technology())
+      .WillByDefault(Return(Technology::kCellular));
+  ON_CALL(*mock_devices_[3].get(), technology())
+      .WillByDefault(Return(Technology::kWifi));
+
+  mock_devices_[0]->SelectService(connected_service1);
+  mock_devices_[1]->SelectService(disconnected_service1);
+  mock_devices_[2]->SelectService(disconnected_service2);
+  mock_devices_[3]->SelectService(connected_service2);
+
+  set<string> expected_technologies;
+  expected_technologies.insert(Technology::NameFromIdentifier(
+      Technology::kEthernet));
+  expected_technologies.insert(Technology::NameFromIdentifier(
+      Technology::kWifi));
+  Error error;
+
+  vector<string> technologies = manager()->ConnectedTechnologies(&error);
+  EXPECT_THAT(set<string>(technologies.begin(), technologies.end()),
+              ContainerEq(expected_technologies));
+}
+
+TEST_F(ManagerTest, DefaultTechnology) {
+  scoped_refptr<MockService> connected_service(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+  scoped_refptr<MockService> disconnected_service(
+      new NiceMock<MockService>(control_interface(),
+                                dispatcher(),
+                                manager()));
+
+  // Connected. WiFi.
+  ON_CALL(*connected_service.get(), IsConnected())
+      .WillByDefault(Return(true));
+  ON_CALL(*connected_service.get(), state())
+      .WillByDefault(Return(Service::kStateConnected));
+  ON_CALL(*connected_service.get(), technology())
+      .WillByDefault(Return(Technology::kWifi));
+
+  // Disconnected. Ethernet.
+  ON_CALL(*disconnected_service.get(), technology())
+      .WillByDefault(Return(Technology::kEthernet));
+
+  manager()->RegisterService(disconnected_service);
+  Error error;
+  EXPECT_THAT(manager()->DefaultTechnology(&error), StrEq(""));
+
+
+  manager()->RegisterService(connected_service);
+  // Connected service should be brought to the front now.
+  string expected_technology =
+      Technology::NameFromIdentifier(Technology::kWifi);
+  EXPECT_THAT(manager()->DefaultTechnology(&error), StrEq(expected_technology));
+}
+
 TEST_F(ManagerTest, DisconnectServicesOnStop) {
   scoped_refptr<MockService> mock_service(
       new NiceMock<MockService>(control_interface(),
@@ -747,8 +887,8 @@
   EXPECT_FALSE(mock_service->favorite());
   EXPECT_FALSE(mock_service->auto_connect());
 
-  EXPECT_CALL(*mock_service.get(), state())
-      .WillRepeatedly(Return(Service::kStateConnected));
+  EXPECT_CALL(*mock_service.get(), IsConnected())
+      .WillRepeatedly(Return(true));
   manager()->UpdateService(mock_service);
   // We can't EXPECT_CALL(..., MakeFavorite), because that requires us
   // to mock out MakeFavorite. And mocking that out would break the
diff --git a/mock_adaptors.h b/mock_adaptors.h
index a722fa7..84819c6 100644
--- a/mock_adaptors.h
+++ b/mock_adaptors.h
@@ -69,6 +69,8 @@
   MOCK_METHOD2(EmitUintChanged, void(const std::string&, uint32));
   MOCK_METHOD2(EmitIntChanged, void(const std::string&, int));
   MOCK_METHOD2(EmitStringChanged, void(const std::string&, const std::string&));
+  MOCK_METHOD2(EmitStringsChanged,
+               void(const std::string &, const std::vector<std::string> &));
   MOCK_METHOD2(EmitRpcIdentifierArrayChanged,
                void(const std::string &, const std::vector<std::string> &));
 
diff --git a/mock_device.cc b/mock_device.cc
index 4e5187a..3abd7b6 100644
--- a/mock_device.cc
+++ b/mock_device.cc
@@ -16,6 +16,7 @@
 class EventDispatcher;
 
 using ::testing::_;
+using ::testing::DefaultValue;
 using ::testing::Return;
 using std::string;
 
@@ -30,8 +31,10 @@
              manager,
              link_name,
              address,
-             interface_index) {
+             interface_index,
+             Technology::kUnknown) {
   ON_CALL(*this, TechnologyIs(_)).WillByDefault(Return(false));
+  DefaultValue<Technology::Identifier>::Set(Technology::kUnknown);
 }
 
 MockDevice::~MockDevice() {}
diff --git a/mock_device.h b/mock_device.h
index b5053ee..7a81659 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -31,6 +31,7 @@
                      bool(const Technology::Identifier technology));
   MOCK_METHOD1(Load, bool(StoreInterface*));
   MOCK_METHOD1(Save, bool(StoreInterface*));
+  MOCK_CONST_METHOD0(technology, Technology::Identifier());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDevice);
diff --git a/mock_service.cc b/mock_service.cc
index 5b44fda..2af9656 100644
--- a/mock_service.cc
+++ b/mock_service.cc
@@ -12,6 +12,7 @@
 
 #include "shill/refptr_types.h"
 #include "shill/store_interface.h"
+#include "shill/technology.h"
 
 using std::string;
 using testing::_;
@@ -26,7 +27,7 @@
 MockService::MockService(ControlInterface *control_interface,
                          EventDispatcher *dispatcher,
                          Manager *manager)
-    : Service(control_interface, dispatcher, manager, "mock") {
+    : Service(control_interface, dispatcher, manager, Technology::kUnknown) {
   const string &id = UniqueName();
   EXPECT_CALL(*this, GetRpcIdentifier()).WillRepeatedly(Return(id));
   EXPECT_CALL(*this, GetStorageIdentifier()).WillRepeatedly(Return(id));
diff --git a/mock_service.h b/mock_service.h
index a9d738a..d58503c 100644
--- a/mock_service.h
+++ b/mock_service.h
@@ -10,6 +10,7 @@
 
 #include "shill/refptr_types.h"
 #include "shill/service.h"
+#include "shill/technology.h"
 
 namespace shill {
 
@@ -26,8 +27,10 @@
   MOCK_METHOD1(CalculateState, std::string(Error *error));
   MOCK_CONST_METHOD1(TechnologyIs,
                      bool(const Technology::Identifier technology));
-  MOCK_METHOD1(SetState, void(ConnectState state));
   MOCK_CONST_METHOD0(state, ConnectState());
+  MOCK_METHOD1(SetState, void(ConnectState state));
+  MOCK_CONST_METHOD0(IsConnected, bool());
+  MOCK_CONST_METHOD0(IsConnecting, bool());
   MOCK_METHOD1(SetFailure, void(ConnectFailure failure));
   MOCK_CONST_METHOD0(failure, ConnectFailure());
   MOCK_METHOD1(GetDeviceRpcId, std::string(Error *error));
@@ -35,7 +38,7 @@
   MOCK_CONST_METHOD0(GetStorageIdentifier, std::string());
   MOCK_METHOD1(Load, bool(StoreInterface *store_interface));
   MOCK_METHOD1(Save, bool(StoreInterface *store_interface));
-
+  MOCK_CONST_METHOD0(technology, Technology::Identifier());
   // Set a string for this Service via |store|.  Can be wired to Save() for
   // test purposes.
   bool FauxSave(StoreInterface *store);
diff --git a/service.cc b/service.cc
index 66131f5..2fc1b3f 100644
--- a/service.cc
+++ b/service.cc
@@ -66,7 +66,7 @@
 Service::Service(ControlInterface *control_interface,
                  EventDispatcher *dispatcher,
                  Manager *manager,
-                 const string &type)
+                 Technology::Identifier technology)
     : state_(kStateUnknown),
       failure_(kFailureUnknown),
       auto_connect_(false),
@@ -77,7 +77,7 @@
       security_level_(0),
       strength_(0),
       save_credentials_(true),
-      type_(type),
+      technology_(technology),
       dispatcher_(dispatcher),
       unique_name_(base::UintToString(serial_number_++)),
       friendly_name_(unique_name_),
@@ -142,13 +142,14 @@
   // store_.RegisterConstStringmap(flimflam::kProviderProperty, &provider_);
 
   store_.RegisterBool(flimflam::kSaveCredentialsProperty, &save_credentials_);
-  store_.RegisterConstString(flimflam::kTypeProperty, &type_);
+  HelpRegisterDerivedString(flimflam::kTypeProperty,
+                            &Service::GetTechnologyString,
+                            NULL);
   // flimflam::kSecurityProperty: Registered in WiFiService
   HelpRegisterDerivedString(flimflam::kStateProperty,
                             &Service::CalculateState,
                             NULL);
   // flimflam::kSignalStrengthProperty: Registered in WiFi/CellularService
-  // flimflam::kTypeProperty: Registered in all derived classes.
   // flimflam::kWifiAuthMode: Registered in WiFiService
   // flimflam::kWifiHiddenSsid: Registered in WiFiService
   // flimflam::kWifiFrequency: Registered in WiFiService
@@ -354,6 +355,10 @@
 
 
 // static
+string Service::GetTechnologyString(Error */*error*/) {
+  return Technology::NameFromIdentifier(technology());
+}
+
 bool Service::DecideBetween(int a, int b, bool *decision) {
   if (a == b)
     return false;
@@ -418,7 +423,9 @@
     case kStateConfiguring:
       return flimflam::kStateConfiguration;
     case kStateConnected:
-      return flimflam::kStateReady;
+      // TODO(gauravsh): Until portal handling is implemented, go to "online"
+      // instead of "ready" state. crosbug.com/23318
+      return flimflam::kStateOnline;
     case kStateDisconnected:
       return flimflam::kStateDisconnect;
     case kStateFailure:
diff --git a/service.h b/service.h
index 7cb1498..a5b5ece 100644
--- a/service.h
+++ b/service.h
@@ -90,7 +90,7 @@
   Service(ControlInterface *control_interface,
           EventDispatcher *dispatcher,
           Manager *manager,
-          const std::string &type);
+          Technology::Identifier technology);
   virtual ~Service();
 
   virtual void Connect(Error *error) = 0;
@@ -110,8 +110,8 @@
   virtual void SetState(ConnectState state);
 
   // State utility functions
-  bool IsConnected() const { return state() == kStateConnected; }
-  bool IsConnecting() const {
+  virtual bool IsConnected() const { return state() == kStateConnected; }
+  virtual bool IsConnecting() const {
     return state() == kStateAssociating || state() == kStateConfiguring;
   }
 
@@ -150,6 +150,9 @@
   bool auto_connect() const { return auto_connect_; }
   void set_auto_connect(bool connect) { auto_connect_ = connect; }
 
+  bool connectable() const { return connectable_; }
+  void set_connectable(bool connectable) { connectable_ = connectable; }
+
   bool favorite() const { return favorite_; }
   // Setter is deliberately omitted; use MakeFavorite.
 
@@ -164,6 +167,9 @@
   int32 strength() const { return strength_; }
   void set_strength(int32 strength) { strength_ = strength; }
 
+  virtual Technology::Identifier technology() const { return technology_; }
+  std::string GetTechnologyString(Error *error);
+
   const std::string &error() const { return error_; }
   void set_error(const std::string &error) { error_ = error; }
 
@@ -279,7 +285,7 @@
   std::string proxy_config_;
   bool save_credentials_;
   EapCredentials eap_;  // Only saved if |save_credentials_| is true.
-  const std::string type_;
+  Technology::Identifier technology_;
 
   ProfileRefPtr profile_;
   PropertyStore store_;
diff --git a/service_under_test.cc b/service_under_test.cc
index dd915da..90aa893 100644
--- a/service_under_test.cc
+++ b/service_under_test.cc
@@ -20,7 +20,7 @@
 ServiceUnderTest::ServiceUnderTest(ControlInterface *control_interface,
                                    EventDispatcher *dispatcher,
                                    Manager *manager)
-    : Service(control_interface, dispatcher, manager, "stub") {
+    : Service(control_interface, dispatcher, manager, Technology::kUnknown) {
 }
 
 ServiceUnderTest::~ServiceUnderTest() {}
diff --git a/technology.cc b/technology.cc
index 1be4184..9e42618 100644
--- a/technology.cc
+++ b/technology.cc
@@ -14,7 +14,7 @@
 
 const char Technology::kUnknownName[] = "Unknown";
 
-
+// static
 Technology::Identifier Technology::IdentifierFromName(const std::string &name) {
   if (name == flimflam::kTypeEthernet) {
     return kEthernet;
@@ -27,6 +27,7 @@
   }
 }
 
+// static
 std::string Technology::NameFromIdentifier(Technology::Identifier id) {
   if (id == kEthernet) {
     return flimflam::kTypeEthernet;
diff --git a/wifi.cc b/wifi.cc
index 34ea231..b0f1c41 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -34,6 +34,7 @@
 #include "shill/store_interface.h"
 #include "shill/supplicant_interface_proxy_interface.h"
 #include "shill/supplicant_process_proxy_interface.h"
+#include "shill/technology.h"
 #include "shill/wifi_endpoint.h"
 #include "shill/wifi_service.h"
 #include "shill/wpa_supplicant.h"
@@ -76,7 +77,8 @@
              manager,
              link,
              address,
-             interface_index),
+             interface_index,
+             Technology::kWifi),
       proxy_factory_(ProxyFactory::GetInstance()),
       task_factory_(this),
       link_up_(false),
diff --git a/wifi_service.cc b/wifi_service.cc
index 7c1748c..a8173a0 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -41,7 +41,7 @@
                          const string &mode,
                          const string &security,
                          bool hidden_ssid)
-    : Service(control_interface, dispatcher, manager, flimflam::kTypeWifi),
+    : Service(control_interface, dispatcher, manager, Technology::kWifi),
       need_passphrase_(false),
       security_(security),
       mode_(mode),