wire-up WiFi class to D-Bus, implement rudimentary scanning support
BUG=chromium-os:14886
TEST=emerge, run wifi_integrationtest on device
Change-Id: I7ff0799d198b4eed0bf92afbef2c5591ca2a388e
Reviewed-on: http://gerrit.chromium.org/gerrit/1585
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/Makefile b/Makefile
index 995efce..db63c09 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@
supplicant-bss.h \
supplicant-interface.h \
supplicant-network.h \
- supplicant-supplicant.h
+ supplicant-process.h
DBUS_HEADERS = $(DBUS_ADAPTOR_HEADERS) $(DBUS_PROXY_HEADERS)
@@ -73,6 +73,7 @@
testrunner.o
all: $(SHILL_BIN) $(TEST_BIN)
+integration_tests: wifi_integrationtest
$(DBUS_PROXY_HEADERS): %.h: %.xml
$(DBUSXX_XML2CPP) $< --proxy=$@
@@ -97,5 +98,11 @@
$(CXX) $(CXXFLAGS) $(TEST_INCLUDE_DIRS) $(TEST_LIB_DIRS) $(LDFLAGS) $^ \
$(TEST_LIBS) -o $@
+# NB(quiche): statically link gmock, gtest, as test device will not have them
+wifi_integrationtest: CXXFLAGS += -DUNIT_TEST
+wifi_integrationtest: wifi_integrationtest.o $(SHILL_LIB)
+ $(CXX) $(CXXFLAGS) $(TEST_INCLUDE_DIRS) $(TEST_LIB_DIRS) $(LDFLAGS) $^ \
+ $(BASE_LIBS) -Wl,-Bstatic -lgmock -lgtest -Wl,-Bdynamic -o $@
+
clean:
rm -rf *.o $(DBUS_HEADERS) $(SHILL_BIN) $(SHILL_LIB) $(TEST_BIN)
diff --git a/README b/README
new file mode 100644
index 0000000..a934c3c
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+wifi_integrationtest:
+- should be run as root
+- should be run on a system with wpa_supplicant running
+- will either report scan results, or fail by timing out after 10 seconds
+- to specify device name, use --device-name=<name>
\ No newline at end of file
diff --git a/supplicant-supplicant.xml b/supplicant-process.xml
similarity index 100%
rename from supplicant-supplicant.xml
rename to supplicant-process.xml
diff --git a/wifi.cc b/wifi.cc
index 7cd7ae3..43cbfc5 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -5,7 +5,9 @@
#include <time.h>
#include <stdio.h>
+#include <map>
#include <string>
+#include <vector>
#include <base/logging.h>
@@ -15,20 +17,158 @@
#include "shill/wifi.h"
+using std::string;
+
namespace shill {
+const char WiFi::kSupplicantPath[] = "/fi/w1/wpa_supplicant1";
+const char WiFi::kSupplicantDBusAddr[] = "fi.w1.wpa_supplicant1";
+const char WiFi::kSupplicantWiFiDriver[] = "nl80211";
+
+WiFi::SupplicantProcessProxy::SupplicantProcessProxy(DBus::Connection *bus)
+ : DBus::ObjectProxy(*bus, kSupplicantPath, kSupplicantDBusAddr) {}
+
+void WiFi::SupplicantProcessProxy::InterfaceAdded(
+ const ::DBus::Path& path,
+ const std::map<string, ::DBus::Variant> &properties) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantProcessProxy::InterfaceRemoved(const ::DBus::Path& path) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantProcessProxy::PropertiesChanged(
+ const std::map<string, ::DBus::Variant>& properties) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+WiFi::SupplicantInterfaceProxy::SupplicantInterfaceProxy(
+ WiFi *wifi,
+ DBus::Connection *bus,
+ const ::DBus::Path &object_path)
+ : wifi_(*wifi),
+ DBus::ObjectProxy(*bus, object_path, kSupplicantDBusAddr) {}
+
+void WiFi::SupplicantInterfaceProxy::ScanDone(const bool& success) {
+ LOG(INFO) << __func__ << " " << success;
+ if (success) {
+ wifi_.ScanDone();
+ }
+}
+
+void WiFi::SupplicantInterfaceProxy::BSSAdded(
+ const ::DBus::Path &BSS,
+ const std::map<string, ::DBus::Variant> &properties) {
+ LOG(INFO) << __func__;
+ wifi_.BSSAdded(BSS, properties);
+}
+
+void WiFi::SupplicantInterfaceProxy::BSSRemoved(const ::DBus::Path &BSS) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::BlobAdded(const string &blobname) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::BlobRemoved(const string &blobname) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::NetworkAdded(
+ const ::DBus::Path &network,
+ const std::map<string, ::DBus::Variant> &properties) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::NetworkRemoved(
+ const ::DBus::Path &network) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::NetworkSelected(
+ const ::DBus::Path &network) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+void WiFi::SupplicantInterfaceProxy::PropertiesChanged(
+ const std::map<string, ::DBus::Variant> &properties) {
+ LOG(INFO) << __func__;
+ // XXX
+}
+
+// NB: we assume supplicant is already running. [quiche.20110518]
WiFi::WiFi(ControlInterface *control_interface,
EventDispatcher *dispatcher,
Manager *manager,
const std::string& link_name,
int interface_index)
: Device(control_interface, dispatcher, manager, link_name,
- interface_index) {
+ interface_index),
+ dbus_(DBus::Connection::SystemBus()), scan_pending_(false) {
VLOG(2) << "WiFi device " << link_name << " initialized.";
}
WiFi::~WiFi() {
}
+void WiFi::Start() {
+ std::map<string, DBus::Variant> create_interface_args;
+ std::map<string, DBus::Variant> scan_args;
+ ::DBus::Path interface_path;
+
+ supplicant_process_proxy_.reset(new SupplicantProcessProxy(&dbus_));
+ create_interface_args["Ifname"].writer().append_string(link_name_.c_str());
+ create_interface_args["Driver"].writer().append_string(kSupplicantWiFiDriver);
+ // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
+ // (file with pkcs config info)
+ interface_path =
+ supplicant_process_proxy_->CreateInterface(create_interface_args);
+ // TODO(quiche) if CreateInterface failed: call GetInterface instead
+ LOG(INFO) << "creating SupplicantInterfaceProxy.";
+ supplicant_interface_proxy_.reset(
+ new SupplicantInterfaceProxy(this, &dbus_, interface_path));
+ // TODO(quiche) set ApScan=1 and BSSExpireAge=190, like flimflam does?
+ scan_args["Type"].writer().append_string("active");
+ LOG(INFO) << "initiating Scan.";
+ // TODO(quiche) indicate scanning in UI
+ supplicant_interface_proxy_->Scan(scan_args);
+ scan_pending_ = true;
+ Device::Start();
+}
+
+void WiFi::BSSAdded(
+ const ::DBus::Path &BSS,
+ const std::map<string, ::DBus::Variant> &properties) {
+ std::vector<uint8_t> ssid = properties.find("SSID")->second;
+ ssids_.push_back(string(ssid.begin(), ssid.end()));
+}
+
+void WiFi::ScanDone() {
+ LOG(INFO) << __func__;
+
+ scan_pending_ = false;
+ for (std::vector<string>::iterator i(ssids_.begin());
+ i != ssids_.end(); ++i) {
+ LOG(INFO) << "found SSID " << *i;
+ }
+}
+
+void WiFi::Stop() {
+ LOG(INFO) << __func__;
+ // XXX
+ Device::Stop();
+}
+
bool WiFi::TechnologyIs(const Device::Technology type) {
return type == Device::kWifi;
}
diff --git a/wifi.h b/wifi.h
index e61e2b2..d962de4 100644
--- a/wifi.h
+++ b/wifi.h
@@ -5,15 +5,18 @@
#ifndef SHILL_WIFI_
#define SHILL_WIFI_
+#include <map>
#include <string>
+#include <vector>
#include "shill/device.h"
#include "shill/shill_event.h"
+#include "shill/supplicant-process.h"
+#include "shill/supplicant-interface.h"
namespace shill {
-// Device superclass. Individual network interfaces types will inherit from
-// this class.
+// WiFi class. Specialization of Device for WiFi.
class WiFi : public Device {
public:
WiFi(ControlInterface *control_interface,
@@ -21,9 +24,85 @@
Manager *manager,
const std::string& link_name,
int interface_index);
- ~WiFi();
- bool TechnologyIs(Device::Technology type);
+ virtual ~WiFi();
+ virtual void Start();
+ virtual void Stop();
+ virtual bool TechnologyIs(const Technology type);
+
+ // called by SupplicantInterfaceProxy, in response to events from
+ // wpa_supplicant.
+ void BSSAdded(const ::DBus::Path &BSS,
+ const std::map<std::string, ::DBus::Variant>
+ &properties);
+ void ScanDone();
+
private:
+ // SupplicantProcessProxy. provides access to wpa_supplicant's
+ // process-level D-Bus APIs.
+ class SupplicantProcessProxy :
+ public fi::w1::wpa_supplicant1_proxy,
+ private ::DBus::ObjectProxy // used by dbus-c++, not WiFi
+ {
+ public:
+ explicit SupplicantProcessProxy(DBus::Connection *bus);
+
+ private:
+ // called by dbus-c++, via wpa_supplicant1_proxy interface,
+ // in response to signals from wpa_supplicant. not exposed
+ // to WiFi.
+ virtual void InterfaceAdded(
+ const ::DBus::Path &path,
+ const std::map<std::string, ::DBus::Variant> &properties);
+ virtual void InterfaceRemoved(const ::DBus::Path &path);
+ virtual void PropertiesChanged(
+ const std::map<std::string, ::DBus::Variant> &properties);
+ };
+
+ // SupplicantInterfaceProxy. provides access to wpa_supplicant's
+ // network-interface D-Bus APIs.
+ class SupplicantInterfaceProxy :
+ public fi::w1::wpa_supplicant1::Interface_proxy,
+ private ::DBus::ObjectProxy // used by dbus-c++, not WiFi
+ {
+ public:
+ SupplicantInterfaceProxy(WiFi *wifi, DBus::Connection *bus,
+ const ::DBus::Path &object_path);
+
+ private:
+ // called by dbus-c++, via Interface_proxy interface,
+ // in response to signals from wpa_supplicant. not exposed
+ // to WiFi.
+ virtual void ScanDone(const bool &success);
+ virtual void BSSAdded(const ::DBus::Path &BSS,
+ const std::map<std::string, ::DBus::Variant>
+ &properties);
+ virtual void BSSRemoved(const ::DBus::Path &BSS);
+ virtual void BlobAdded(const std::string &blobname);
+ virtual void BlobRemoved(const std::string &blobname);
+ virtual void NetworkAdded(const ::DBus::Path &network,
+ const std::map<std::string, ::DBus::Variant>
+ &properties);
+ virtual void NetworkRemoved(const ::DBus::Path &network);
+ virtual void NetworkSelected(const ::DBus::Path &network);
+ virtual void PropertiesChanged(const std::map<std::string, ::DBus::Variant>
+ &properties);
+
+ WiFi &wifi_;
+ };
+
+ static const char kSupplicantPath[];
+ static const char kSupplicantDBusAddr[];
+ static const char kSupplicantWiFiDriver[];
+
+ DBus::Connection dbus_;
+ scoped_ptr<SupplicantProcessProxy> supplicant_process_proxy_;
+ scoped_ptr<SupplicantInterfaceProxy> supplicant_interface_proxy_;
+ bool scan_pending_;
+ std::vector<std::string> ssids_;
+
+ // provide WiFiTest access to scan_pending_, so it can determine
+ // if the scan completed, or timed out.
+ friend class WiFiTest;
DISALLOW_COPY_AND_ASSIGN(WiFi);
};
diff --git a/wifi_integrationtest.cc b/wifi_integrationtest.cc
new file mode 100644
index 0000000..969d845
--- /dev/null
+++ b/wifi_integrationtest.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <iostream>
+#include <string>
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <gtest/gtest.h>
+
+#include <glib.h>
+#include <dbus-c++/dbus.h>
+#include <dbus-c++/glib-integration.h>
+
+#include "shill/dbus_control.h"
+#include "shill/device_info.h"
+#include "shill/wifi.h"
+
+namespace switches {
+// wi-fi device name
+static const char kDeviceName[] = "device-name";
+// Flag that causes shill to show the help message and exit.
+static const char kHelp[] = "help";
+// The help message shown if help flag is passed to the program.
+static const char kHelpMessage[] = "\n"
+ "Switches for " __FILE__ "\n"
+ " --device-name\n"
+ " name of wi-fi device (e.g. wlan0).\n";
+} // namespace switches
+
+namespace shill {
+using ::testing::Test;
+
+const int kInterfaceIndexUnknown = -1;
+const unsigned int kScanTimeoutSecs = 60;
+const char kDefaultDeviceName[] = "wlan0";
+DBus::Glib::BusDispatcher dbus_glib_dispatcher;
+std::string device_name;
+
+class WiFiTest : public Test {
+ public:
+ WiFiTest() : timed_out_(false) {
+ wifi_ = new WiFi(&dbus_control_, NULL, NULL, device_name,
+ kInterfaceIndexUnknown);
+ }
+
+ bool ScanPending() {
+ return wifi_->scan_pending_;
+ }
+
+ void TimeOut() {
+ timed_out_ = true;
+ }
+
+ ~WiFiTest() {
+ wifi_->Release();
+ }
+
+ static gboolean TimeoutHandler(void *test_instance) {
+ static_cast<WiFiTest *>(test_instance)->TimeOut();
+ return false;
+ }
+
+ protected:
+ DBusControl dbus_control_;
+ WiFi *wifi_;
+ bool timed_out_;
+};
+
+
+TEST_F(WiFiTest, SSIDScanning) {
+ wifi_->Start();
+ g_timeout_add_seconds(10, WiFiTest::TimeoutHandler, this);
+
+ // Crank the glib main loop
+ while (ScanPending() && !timed_out_) {
+ LOG(INFO) << "cranking event loop";
+ g_main_context_iteration(NULL, TRUE);
+ }
+
+ ASSERT_FALSE(timed_out_);
+}
+
+} // namespace shill
+
+int main(int argc, char **argv) {
+ base::AtExitManager exit_manager;
+ ::testing::InitGoogleTest(&argc, argv);
+ CommandLine::Init(argc, argv);
+ CommandLine *cl = CommandLine::ForCurrentProcess();
+
+ if (cl->HasSwitch(switches::kHelp)) {
+ // NB(quiche): google test prints test framework help message
+ // at InitGoogleTest, above.
+ std::cout << switches::kHelpMessage;
+ return 0;
+ }
+
+ if (cl->HasSwitch(switches::kDeviceName)) {
+ shill::device_name =
+ cl->GetSwitchValueASCII(switches::kDeviceName);
+ } else {
+ shill::device_name = shill::kDefaultDeviceName;
+ }
+
+ shill::dbus_glib_dispatcher.attach(NULL); // NULL => default context
+ DBus::default_dispatcher = &shill::dbus_glib_dispatcher;
+ return RUN_ALL_TESTS();
+}