shill: Add a command line option that controls the default technology order

This can be used to configure a preference for a particular network on a given
device.  For example, a device may want to use the mobile network for all
traffic even if an open wifi network is available.

BUG=chromium:453607
TEST=Added the flag via /etc/init/shill.override and confirmed that dbus GetServiceOrder returned the new order

Change-Id: Ifac0baa4feeaae4a780d4d74f2bd195b4e4dd31d
Reviewed-on: https://chromium-review.googlesource.com/248610
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Jason Simmons <jsimmons@chromium.org>
Commit-Queue: Jason Simmons <jsimmons@chromium.org>
diff --git a/manager.cc b/manager.cc
index 37ebdc9..166b326 100644
--- a/manager.cc
+++ b/manager.cc
@@ -100,7 +100,8 @@
                  GLib *glib,
                  const string &run_directory,
                  const string &storage_directory,
-                 const string &user_storage_directory)
+                 const string &user_storage_directory,
+                 const vector<Technology::Identifier> &default_technology_order)
     : dispatcher_(dispatcher),
       run_path_(FilePath(run_directory)),
       storage_path_(FilePath(storage_directory)),
@@ -195,13 +196,8 @@
                                   &Manager::UninitializedTechnologies);
   store_.RegisterBool(kWakeOnLanEnabledProperty, &is_wake_on_lan_enabled_);
 
-  // Set default technology order "by hand", to avoid invoking side
-  // effects of SetTechnologyOrder.
-  technology_order_.push_back(Technology::IdentifierFromName(kTypeVPN));
-  technology_order_.push_back(Technology::IdentifierFromName(kTypeEthernet));
-  technology_order_.push_back(Technology::IdentifierFromName(kTypeWifi));
-  technology_order_.push_back(Technology::IdentifierFromName(kTypeWimax));
-  technology_order_.push_back(Technology::IdentifierFromName(kTypeCellular));
+  // Do not invoke SetTechnologyOrder here because of its side effects.
+  technology_order_ = default_technology_order;
 
   UpdateProviderMapping();
 
diff --git a/manager.h b/manager.h
index f73ee4e..1c99b7e 100644
--- a/manager.h
+++ b/manager.h
@@ -89,7 +89,8 @@
           GLib *glib,
           const std::string &run_directory,
           const std::string &storage_directory,
-          const std::string &user_storage_directory);
+          const std::string &user_storage_directory,
+          const std::vector<Technology::Identifier> &default_technology_order);
   virtual ~Manager();
 
   void AddDeviceToBlackList(const std::string &device_name);
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 8897952..f9b9e16 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -617,7 +617,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  string());
+                  string(),
+                  default_technology_order());
   ProfileRefPtr profile(CreateProfileForManager(&manager, &glib));
   ASSERT_TRUE(profile.get());
   AdoptProfile(&manager, profile);
@@ -670,7 +671,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  string());
+                  string(),
+                  default_technology_order());
   ProfileRefPtr profile(CreateProfileForManager(&manager, &glib));
   ASSERT_TRUE(profile.get());
   AdoptProfile(&manager, profile);
@@ -703,7 +705,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  string());
+                  string(),
+                  default_technology_order());
   ProfileRefPtr profile(CreateProfileForManager(&manager, &glib));
   ASSERT_TRUE(profile.get());
   AdoptProfile(&manager, profile);
@@ -805,7 +808,8 @@
                   glib(),
                   run_path(),
                   storage_path(),
-                  string());
+                  string(),
+                  default_technology_order());
   scoped_refptr<MockService> s2(new MockService(control_interface(),
                                                 dispatcher(),
                                                 metrics(),
@@ -929,7 +933,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  temp_dir.path().value());
+                  temp_dir.path().value(),
+                  default_technology_order());
 
   // Invalid name should be rejected.
   EXPECT_EQ(Error::kInvalidArguments, TestCreateProfile(&manager, ""));
@@ -969,7 +974,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  temp_dir.path().value());
+                  temp_dir.path().value(),
+                  default_technology_order());
   vector<ProfileRefPtr> &profiles = GetProfiles(&manager);
 
   // Pushing an invalid profile should fail.
@@ -1145,7 +1151,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  temp_dir.path().value());
+                  temp_dir.path().value(),
+                  default_technology_order());
 
   const char kProfile0[] = "profile0";
   FilePath profile_path(
@@ -1259,7 +1266,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  temp_dir.path().value());
+                  temp_dir.path().value(),
+                  default_technology_order());
 
   const char kProfile0[] = "profile0";
   FilePath profile_path(
@@ -4310,7 +4318,8 @@
                   &glib,
                   run_path(),
                   storage_path(),
-                  temp_dir.path().value());
+                  temp_dir.path().value(),
+                  default_technology_order());
   // Can't use |wifi_provider_|, because it's owned by the Manager
   // object in the fixture.
   MockWiFiProvider *wifi_provider = new NiceMock<MockWiFiProvider>();
@@ -4366,7 +4375,8 @@
                             &glib,
                             run_path(),
                             temp_dir.path().value(),
-                            temp_dir.path().value()));
+                            temp_dir.path().value(),
+                            default_technology_order()));
   manager->InitializeProfiles();
   EXPECT_EQ(PortalDetector::kDefaultCheckPortalList,
             manager->props_.check_portal_list);
@@ -4393,7 +4403,8 @@
                             &glib,
                             run_path(),
                             temp_dir.path().value(),
-                            temp_dir.path().value()));
+                            temp_dir.path().value(),
+                            default_technology_order()));
   manager->InitializeProfiles();
   EXPECT_EQ(kCustomCheckPortalList, manager->props_.check_portal_list);
 
@@ -4406,7 +4417,8 @@
                             &glib,
                             run_path(),
                             temp_dir.path().value(),
-                            temp_dir.path().value()));
+                            temp_dir.path().value(),
+                            default_technology_order()));
   manager->InitializeProfiles();
   EXPECT_EQ(PortalDetector::kDefaultCheckPortalList,
             manager->props_.check_portal_list);
@@ -4425,7 +4437,8 @@
                             &glib,
                             run_path(),
                             temp_dir.path().value(),
-                            temp_dir.path().value()));
+                            temp_dir.path().value(),
+                            default_technology_order()));
 
   ScopedMockLog log;
   EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
diff --git a/mock_manager.cc b/mock_manager.cc
index 907dc6d..e142337 100644
--- a/mock_manager.cc
+++ b/mock_manager.cc
@@ -4,8 +4,11 @@
 
 #include "shill/mock_manager.h"
 
+#include <vector>
+
 #include <gmock/gmock.h>
 
+using std::vector;
 using testing::Invoke;
 
 namespace shill {
@@ -14,7 +17,8 @@
                          EventDispatcher *dispatcher,
                          Metrics *metrics,
                          GLib *glib)
-    : Manager(control_interface, dispatcher, metrics, glib, "", "", ""),
+    : Manager(control_interface, dispatcher, metrics, glib, "", "", "",
+              vector<Technology::Identifier>()),
       mock_device_info_(nullptr) {
   EXPECT_CALL(*this, device_info())
       .WillRepeatedly(Invoke(this, &MockManager::mock_device_info));
diff --git a/property_store_unittest.cc b/property_store_unittest.cc
index 7de166c..30be06f 100644
--- a/property_store_unittest.cc
+++ b/property_store_unittest.cc
@@ -79,13 +79,19 @@
       invalid_prop_(kErrorResultInvalidProperty),
       path_(dir_.CreateUniqueTempDir() ? dir_.path().value() : ""),
       metrics_(dispatcher()),
+      default_technology_order_{Technology::kVPN,
+                                Technology::kEthernet,
+                                Technology::kWifi,
+                                Technology::kWiMax,
+                                Technology::kCellular},
       manager_(control_interface(),
                dispatcher(),
                metrics(),
                glib(),
                run_path(),
                storage_path(),
-               string()) {
+               string(),
+               default_technology_order()) {
 }
 
 PropertyStoreTest::~PropertyStoreTest() {}
diff --git a/property_store_unittest.h b/property_store_unittest.h
index 6b1ee05..c63175f 100644
--- a/property_store_unittest.h
+++ b/property_store_unittest.h
@@ -181,6 +181,9 @@
   EventDispatcher *dispatcher() { return &dispatcher_; }
   MockGLib *glib() { return &glib_; }
   MockMetrics *metrics() { return &metrics_; }
+  const std::vector<Technology::Identifier> &default_technology_order() {
+    return default_technology_order_;
+  }
 
   const std::string &run_path() const { return path_; }
   const std::string &storage_path() const { return path_; }
@@ -199,6 +202,7 @@
   EventDispatcher dispatcher_;
   testing::NiceMock<MockMetrics> metrics_;
   MockGLib glib_;
+  const std::vector<Technology::Identifier> default_technology_order_;
   Manager manager_;
 };
 
diff --git a/shill_daemon.cc b/shill_daemon.cc
index ab82567..00cf307 100644
--- a/shill_daemon.cc
+++ b/shill_daemon.cc
@@ -23,6 +23,7 @@
 using base::Bind;
 using base::Unretained;
 using std::string;
+using std::vector;
 
 namespace shill {
 
@@ -32,7 +33,8 @@
 }
 
 
-Daemon::Daemon(Config *config, ControlInterface *control)
+Daemon::Daemon(Config *config, ControlInterface *control,
+               const vector<Technology::Identifier> &default_technology_order)
     : config_(config),
       control_(control),
       metrics_(new Metrics(&dispatcher_)),
@@ -47,7 +49,8 @@
                            &glib_,
                            config->GetRunDirectory(),
                            config->GetStorageDirectory(),
-                           config->GetUserStorageDirectory())),
+                           config->GetUserStorageDirectory(),
+                           default_technology_order)),
       callback80211_metrics_(metrics_.get()) {
 }
 
diff --git a/shill_daemon.h b/shill_daemon.h
index c886192..d2cbf19 100644
--- a/shill_daemon.h
+++ b/shill_daemon.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "shill/control_interface.h"
 #include "shill/event_dispatcher.h"
@@ -28,7 +29,8 @@
 
 class Daemon {
  public:
-  Daemon(Config *config, ControlInterface *control);
+  Daemon(Config *config, ControlInterface *control,
+         const std::vector<Technology::Identifier> &default_technology_order);
   ~Daemon();
 
   void AddDeviceToBlackList(const std::string &device_name);
diff --git a/shill_main.cc b/shill_main.cc
index c20f6fa..4d98f06 100644
--- a/shill_main.cc
+++ b/shill_main.cc
@@ -19,6 +19,7 @@
 #include <chromeos/syslog_logging.h>
 
 #include "shill/dbus_control.h"
+#include "shill/error.h"
 #include "shill/glib_io_handler_factory.h"
 #include "shill/logging.h"
 #include "shill/net/io_handler_factory_container.h"
@@ -44,6 +45,8 @@
 // Remote service can instruct Shill to manage/unmanage devices through
 // org.chromium.flimflam.Manager's ClaimInterface/ReleaseInterface APIs.
 static const char kPassiveMode[] = "passive-mode";
+// Default priority order of the technologies.
+static const char kDefaultTechnologyOrder[] = "default-technology-order";
 // Flag that causes shill to show the help message and exit.
 static const char kHelp[] = "help";
 
@@ -65,7 +68,9 @@
     "  --portal-list=technology1,technology2\n"
     "    Specify technologies to perform portal detection on at startup.\n"
     "  --passive-mode\n"
-    "    Do not manage any devices by default\n";
+    "    Do not manage any devices by default\n"
+    "  --default-technology-order=technology1,technology2\n"
+    "    Specify the default priority order of the technologies.\n";
 }  // namespace switches
 
 namespace {
@@ -153,10 +158,34 @@
   shill::DBusControl* dbus_control = new shill::DBusControl();
   dbus_control->Init();
 
+  vector<shill::Technology::Identifier> technology_order;
+  if (cl->HasSwitch(switches::kDefaultTechnologyOrder)) {
+    shill::Error error;
+    string order_flag = cl->GetSwitchValueASCII(
+        switches::kDefaultTechnologyOrder);
+    if (!shill::Technology::GetTechnologyVectorFromString(
+        order_flag, &technology_order, &error)) {
+      LOG(ERROR) << "Invalid default technology order: [" << order_flag
+                 << "] Error: " << error.message();
+    }
+  }
+  if (technology_order.empty()) {
+    technology_order.push_back(
+        shill::Technology::IdentifierFromName(shill::kTypeVPN));
+    technology_order.push_back(
+        shill::Technology::IdentifierFromName(shill::kTypeEthernet));
+    technology_order.push_back(
+        shill::Technology::IdentifierFromName(shill::kTypeWifi));
+    technology_order.push_back(
+        shill::Technology::IdentifierFromName(shill::kTypeWimax));
+    technology_order.push_back(
+        shill::Technology::IdentifierFromName(shill::kTypeCellular));
+  }
+
   shill::Config config;
 
   // Passes ownership of dbus_control.
-  shill::Daemon daemon(&config, dbus_control);
+  shill::Daemon daemon(&config, dbus_control, technology_order);
 
   if (cl->HasSwitch(switches::kDeviceBlackList)) {
     vector<string> device_list;
diff --git a/shill_unittest.cc b/shill_unittest.cc
index 96e7ad7..b1fb375 100644
--- a/shill_unittest.cc
+++ b/shill_unittest.cc
@@ -5,6 +5,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
 
 #include <base/bind.h>
 #include <base/cancelable_callback.h>
@@ -33,6 +34,7 @@
 using base::CancelableClosure;
 using base::Unretained;
 using base::WeakPtrFactory;
+using std::vector;
 
 using ::testing::Expectation;
 using ::testing::Gt;
@@ -183,7 +185,8 @@
 class ShillDaemonTest : public Test {
  public:
   ShillDaemonTest()
-      : daemon_(&config_, new MockControl()),  // Passes ownership.
+      : daemon_(&config_, new MockControl(),
+                vector<Technology::Identifier>()),  // Passes ownership.
         metrics_(new MockMetrics(&daemon_.dispatcher_)),
         manager_(new MockManager(daemon_.control_.get(),
                                  &daemon_.dispatcher_,